<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>bit-ranger</title>
    <description></description>
    <link>https://bit-ranger.github.io/blog/</link>
    <atom:link href="https://bit-ranger.github.io/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 14 Oct 2021 15:09:46 +0000</pubDate>
    <lastBuildDate>Thu, 14 Oct 2021 15:09:46 +0000</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>大文件内容对比多线程版本</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#线程池&quot; id=&quot;markdown-toc-线程池&quot;&gt;线程池&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#线程池消费拆分任务&quot; id=&quot;markdown-toc-线程池消费拆分任务&quot;&gt;线程池消费拆分任务&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#线程池消费合并任务&quot; id=&quot;markdown-toc-线程池消费合并任务&quot;&gt;线程池消费合并任务&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#测试&quot; id=&quot;markdown-toc-测试&quot;&gt;测试&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是&lt;a href=&quot;https://bit-ranger.github.io/blog/algorithm/large-file-diff/&quot;&gt;上一篇&lt;/a&gt;的续作，对于这个算法，其中可以同时进行的部分有&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;拆分后对每一个块的排序可以同时进行&lt;/li&gt;
  &lt;li&gt;合并时的不同范围之间可以同时进行，例如拆分为10个小块，那么1-5小块的合并跟6-10小块的合并过程可以同时进行&lt;/li&gt;
  &lt;li&gt;合并的不同阶段之间不可以同时进行，因为不同阶段之间有先后顺序&lt;/li&gt;
  &lt;li&gt;不存在对同一条数据的修改，所以无需进行并发控制&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;线程池&quot;&gt;线程池&lt;/h1&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;threadPoolExecutor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10L&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;SECONDS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedBlockingQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CustomizableThreadFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fileSorterTPE-&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;CallerRunsPolicy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;线程池消费拆分任务&quot;&gt;线程池消费拆分任务&lt;/h1&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;splitFutureList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;br&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialChunkSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rowNum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;rowNum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threadPoolExecutor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialChunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;splitFutureList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chunkList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;splitFutureList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;线程池消费合并任务&quot;&gt;线程池消费合并任务&lt;/h1&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentLevel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;INITIAL_CHUNK_LEVEL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mergeFutureList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//从队列中获取一组chunk&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunkQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//未取到同级chunk, 表示此级别应合并完成&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CollectionUtils&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mergeFutureList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;chunkQueue:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mergeFutureList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//chunkQueue 中只有一个元素，表示此次合并是最终合并&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunkQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;currentLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threadPoolExecutor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mergeFutureList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到合并任务与拆分任务有些不同，拆分任务是在循环退出后才执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Future.get&lt;/code&gt;，因为拆分不用考虑先后；
而合并任务在每次获取当前阶段的chunk结束时执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Future.get&lt;/code&gt;，这样才能避免不同的阶段之间产生混乱。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bit-ranger/architecture/blob/d9083d2fb71763557e6d4eb6875f9c001fd41596/core/src/main/java/com/rainyalley/architecture/core/arithmetic/sort/FileSorter.java&quot;&gt;完整代码&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;测试&quot;&gt;测试&lt;/h1&gt;

&lt;p&gt;在上一篇中，使用单线程，1千万条数据排序耗时13秒；
在同一台电脑上，使用多线程后，耗时6秒，时间减少了一半。&lt;/p&gt;

&lt;p&gt;在拆分过程中，每个线程都要在内存中进行排序，
在拆分和合并过程中，每个线程都要持有自己的读写缓冲区，这无疑会增大内存的使用量。&lt;/p&gt;

&lt;p&gt;究竟消耗了多少内存，我们可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Java Mission Control&lt;/code&gt;来观察，jdk8的bin目录下&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jmc.exe&lt;/code&gt;即为此工具。&lt;/p&gt;
</description>
        <pubDate>Thu, 28 May 2015 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/algorithm/large-file-diff-concurrent/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/algorithm/large-file-diff-concurrent/</guid>
        
        <category>java</category>
        
        <category>算法</category>
        
        <category>algorithm</category>
        
        <category>排序</category>
        
        <category>concurrent</category>
        
        
        <category>algorithm</category>
        
      </item>
    
      <item>
        <title>大文件内容对比</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#大文件内容排序&quot; id=&quot;markdown-toc-大文件内容排序&quot;&gt;大文件内容排序&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#测试&quot; id=&quot;markdown-toc-测试&quot;&gt;测试&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最近接到一个需求，要对两个系统的订单进行对比，找出其中的差异，对比结果有4种：一致、不一致、丢失、多余。&lt;/p&gt;

&lt;p&gt;如果数据量少，处理起来就很简单，例如要对A,B两份数据进行对比：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;将数据A放入哈希表&lt;/li&gt;
  &lt;li&gt;遍历数据B，依据id从A中查找对应的订单&lt;/li&gt;
  &lt;li&gt;若从A中找到了对应的订单，则比较是否一致，并将此订单标记为已匹配&lt;/li&gt;
  &lt;li&gt;若从A中找不到对应的订单，则表示A丢失此订单&lt;/li&gt;
  &lt;li&gt;遍历A，筛选出所有未匹配的订单，这些订单都是A中多余的&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;但是如果数据量超大，在内存中处理就不合适了，数据需要放在磁盘上。
基于哈希的对比算法，无法避免大量对磁盘的随机访问，因而无法使用buffer，完全的io读写性能太差，所以这个方案是不行的。&lt;/p&gt;

&lt;p&gt;如果AB两个集合是有序的，那么对比将会变得容易，一次遍历就可以对比完成，例如以id排序&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;读取A指针对应的订单，读取B指针对应的订单&lt;/li&gt;
  &lt;li&gt;若两个订单id相等，则对比内容是否一致&lt;/li&gt;
  &lt;li&gt;若A指针订单id&amp;gt;B指针订单id，代表A中缺少B指针订单，记录结果，B指针移动到后一个&lt;/li&gt;
  &lt;li&gt;若A指针订单id&amp;lt;B指针订单id，代表A中多余了指针订单，记录结果，A指针移动到后一个&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以，这个问题可以拆分为：对A、B文件内容进行排序，双指针遍历 A、B。&lt;/p&gt;

&lt;h1 id=&quot;大文件内容排序&quot;&gt;大文件内容排序&lt;/h1&gt;

&lt;p&gt;对大文件的内容进行排序，可以使用拆分-&amp;gt;排序-&amp;gt;合并的思路&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;按行读取文件，将读到的行放入list&lt;/li&gt;
  &lt;li&gt;如果已读取的行达到了1万行，对list进行排序，将排序后的list逐行写入临时文件&lt;/li&gt;
  &lt;li&gt;打开多个临时文件，从每个文件的中逐行读取，选取其中订单id最小的行，写入新的临时文件&lt;/li&gt;
  &lt;li&gt;循环以上步骤，直至剩下一个文件&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;示例代码&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;br&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialChunkSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
             &lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialChunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowNum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;chunkList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;rowNum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;chunkRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
         &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
     &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunkQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CollectionUtils&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)){&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;currentLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++;&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;//合并&lt;/span&gt;
     &lt;span class=&quot;nc&quot;&gt;Chunk&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mergedChunk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollChunks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;//chunkQueue 中没有后续的元素，表示此次合并是最终合并&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunkQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;chunkQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mergedChunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;chunkQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mergedChunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;完整代码&quot;&gt;完整代码&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;测试&quot;&gt;测试&lt;/h1&gt;

&lt;p&gt;在最后的多路合并中要考虑使用多少路进行合并，更少的路数，将产生更多的临时文件&lt;/p&gt;

&lt;p&gt;拆分时需要考虑每个块的大小，过大的块将造成频繁的FULL GC&lt;/p&gt;

&lt;p&gt;IO的缓冲容量区也要考虑，这将影响IO读写的速度，以及FULL GC的频率&lt;/p&gt;

&lt;p&gt;以如下配置为例，在我的个人电脑上， 
cpu频率3.40GHZ，磁盘顺序读450MB/s，随机读190MB/s，顺序写250MB/s，数据宽度20为20字符
对1千万条数据进行排序，耗时13秒。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;FileSorter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sorter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileSorter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/var/tmp/fileSorter&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sorter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;该排序算法既有CPU计算又有IO，CPU计算时IO空闲，IO时CPU空间，如果把该方法改写成多线程版本，理论上可以提升不少速度。&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Mar 2015 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/algorithm/large-file-diff/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/algorithm/large-file-diff/</guid>
        
        <category>java</category>
        
        <category>算法</category>
        
        <category>algorithm</category>
        
        <category>排序</category>
        
        
        <category>algorithm</category>
        
      </item>
    
      <item>
        <title>大对象引起的频繁FULL GC</title>
        <description>&lt;p&gt;最近发现了频繁FULL GC的情况，查看GC日志后，发现每次FULL GC后，老年代都能回收大半以上的空间，这意味着有很多临时对象被分配到了老年代。&lt;/p&gt;

&lt;p&gt;用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jmap&lt;/code&gt;命令导出堆转储文件，然后用jvisualvm导入后进行分析，发现char[]占据了最多了内存。&lt;/p&gt;

&lt;p&gt;其中有大量的对象达到了3M，被直接担保分配进了老年代，因为暂时不知道这些对象是怎么产生的，最快的解决办法就是增大担保分配的阈值。&lt;/p&gt;

&lt;p&gt;下面就是测试过程&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 -server
 -XX:+UseConcMarkSweepGC
 -Xms20M
 -Xmx20M
 -Xmn10M
 -XX:SurvivorRatio=8
 -XX:PretenureSizeThreshold=2000000
 -XX:+PrintGC
 -XX:+PrintGCDetails
 -XX:+PrintGCDateStamps
 -XX:+PrintHeapAtGC
 -Xloggc:/var/logs/PretenureSizeThreshold.log
 -XX:+HeapDumpOnOutOfMemoryError
 -XX:HeapDumpPath=/var/dumps/PretenureSizeThreshold.hprof
 */&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PretenureSizeThreshold&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_1MB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;RuntimeMXBean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mxBean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;ManagementFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getRuntimeMXBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mxBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mxBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allocation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_1MB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;D:\Doc\MyRepo\learning-java&amp;gt;jmap -heap 14000
Attaching to process ID 14000, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 20971520 (20.0MB)
   NewSize                  = 10485760 (10.0MB)
   MaxNewSize               = 10485760 (10.0MB)
   OldSize                  = 10485760 (10.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 9437184 (9.0MB)
   used     = 2748888 (2.6215438842773438MB)
   free     = 6688296 (6.378456115722656MB)
   29.128265380859375% used
Eden Space:
   capacity = 8388608 (8.0MB)
   used     = 2748888 (2.6215438842773438MB)
   free     = 5639720 (5.378456115722656MB)
   32.7692985534668% used
From Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
To Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 10485760 (10.0MB)
   used     = 3000016 (2.8610382080078125MB)
   free     = 7485744 (7.1389617919921875MB)
   28.610382080078125% used

1815 interned Strings occupying 162040 bytes.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;老年代占用的空间为3000016，这表示数组通过担保分配进入了老年代，多出来的16是对象头的体积&lt;/p&gt;

&lt;p&gt;修改启动参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-XX:PretenureSizeThreshold=4000000&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;D:\Doc\MyRepo\learning-java&amp;gt;jmap -heap 2356
Attaching to process ID 2356, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 20971520 (20.0MB)
   NewSize                  = 10485760 (10.0MB)
   MaxNewSize               = 10485760 (10.0MB)
   OldSize                  = 10485760 (10.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 9437184 (9.0MB)
   used     = 5748624 (5.4823150634765625MB)
   free     = 3688560 (3.5176849365234375MB)
   60.91461181640625% used
Eden Space:
   capacity = 8388608 (8.0MB)
   used     = 5748624 (5.4823150634765625MB)
   free     = 2639984 (2.5176849365234375MB)
   68.52893829345703% used
From Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
To Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 10485760 (10.0MB)
   used     = 0 (0.0MB)
   free     = 10485760 (10.0MB)
   0.0% used

1815 interned Strings occupying 162040 bytes.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;此时，老年代占用0，这表示数组没有达到大对象的标准，直接分配在了新生代&lt;/p&gt;
</description>
        <pubDate>Sun, 15 Mar 2015 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/java/FullGc/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/java/FullGc/</guid>
        
        <category>java</category>
        
        <category>jvm</category>
        
        
        <category>java</category>
        
      </item>
    
      <item>
        <title>spring security 探秘</title>
        <description>&lt;h1 id=&quot;概述&quot;&gt;概述&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;http://projects.spring.io/spring-security&quot;&gt;Spring Security&lt;/a&gt;这是一种基于Spring AOP和Servlet过滤器的安全框架。它提供全面的安全性解决方案，同时在Web请求级和方法调用级处理身份确认和授权。在Spring Framework基础上，Spring Security充分利用了依赖注入（DI，Dependency Injection）和面向切面技术。&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#概述&quot; id=&quot;markdown-toc-概述&quot;&gt;概述&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#filter&quot; id=&quot;markdown-toc-filter&quot;&gt;Filter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#http&quot; id=&quot;markdown-toc-http&quot;&gt;HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#filterchain&quot; id=&quot;markdown-toc-filterchain&quot;&gt;FilterChain&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#filtersecurityinterceptor&quot; id=&quot;markdown-toc-filtersecurityinterceptor&quot;&gt;FilterSecurityInterceptor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#authenticationmanager&quot; id=&quot;markdown-toc-authenticationmanager&quot;&gt;AuthenticationManager&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#accessdecisionmanager&quot; id=&quot;markdown-toc-accessdecisionmanager&quot;&gt;AccessDecisionManager&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#securitymetadatasource&quot; id=&quot;markdown-toc-securitymetadatasource&quot;&gt;SecurityMetadataSource&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文的宗旨并非描述如何从零开始搭建一个 “hello world” 级的demo，或者列举有哪些可配置项（这种类似于词典的文档，没有比&lt;a href=&quot;http://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/&quot;&gt;参考书&lt;/a&gt;更合适的了），而是简单描述spring-security项目的整体结构，设计思想，以及某些重要配置做了什么。&lt;/p&gt;

&lt;p&gt;本文所有内容基于spring-security-4.0.1.RELEASE ,你可以在&lt;a href=&quot;https://github.com/spring-projects/spring-security&quot;&gt;Github&lt;/a&gt;中找到它，或者使用Maven获取，引入spring-security-config是为了通过命名空间简化配置。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.security&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-security-web&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;4.0.1.RELEASE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.security&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-security-config&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;4.0.1.RELEASE&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;filter&quot;&gt;Filter&lt;/h1&gt;

&lt;p&gt;spring-security的业务流程是独立于项目的，我们需要在web.xml中指定其入口，注意该过滤器必须在项目的过滤器之前。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;filter&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;filter-name&amp;gt;&lt;/span&gt;springSecurityFilterChain&lt;span class=&quot;nt&quot;&gt;&amp;lt;/filter-name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;filter-class&amp;gt;&lt;/span&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;span class=&quot;nt&quot;&gt;&amp;lt;/filter-class&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;filter-mapping&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;filter-name&amp;gt;&lt;/span&gt;springSecurityFilterChain&lt;span class=&quot;nt&quot;&gt;&amp;lt;/filter-name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;/*&lt;span class=&quot;nt&quot;&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/filter-mapping&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;值得一提的是，该过滤器的名字具有特殊意义，没有特别需求不建议修改，我们可以在该过滤的源码中看到，其过滤行为委托给了一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate&lt;/code&gt;对象，该delegate对象是一个从spring容器中获取的bean，依据的beanid就是filter-name。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initFilterBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServletException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delegateMonitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;targetBeanName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;targetBeanName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getFilterName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;nc&quot;&gt;WebApplicationContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findWebApplicationContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initDelegate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wac&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;http&quot;&gt;HTTP&lt;/h1&gt;

&lt;p&gt;我们可以在security中声明多个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt;元素，每个http元素将产生一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilterChain&lt;/code&gt;，这些FilterChain将按照声明顺序加入到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilterChainProxy&lt;/code&gt;中，而这个FilterChainProxy就是web.xml中定义的springSecurityFilterChain内部的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;security:http&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;security=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;none&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/favicon.ico&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;security:http&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;security=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;none&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/resources/**&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;security:http&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;security=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;none&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/user/login&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在http元素也就是FilterChain中，以责任链的形式存在多个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter&lt;/code&gt;，这些Filter真正执行过滤操作，http标签中的许多配置项，如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; &amp;lt;security:http-basic/&amp;gt;&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;security:logout/&amp;gt;&lt;/code&gt;等，其实就是创建指定的Filter，以下表格列举了这些Filter。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/spring-security-filter.png&quot; alt=&quot;filter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;利用别名，我们可以将自定义的过滤器加入指定的位置，或者替换其中的某个过滤器。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;security:custom-filter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;filterSecurityInterceptor&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;before=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FILTER_SECURITY_INTERCEPTOR&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;整体来看，一个FilterChainProxy中可以包含有多个FilterChain，一个FilterChain中又可以包含有多个Filter，然而对于一个既定请求，只会使用其中一个FilterChain。&lt;/p&gt;

&lt;h1 id=&quot;filterchain&quot;&gt;FilterChain&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/spring-security-filterChain.jpg&quot; alt=&quot;filterChain&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图列举了一些Filter, 此处将说明这些Filter的作用, 在需要插入自定义Filter时, 这些说明可以作为参考。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;SecurityContextPersistenceFilter
  创建一个空的SecurityContext（如果session中没有SecurityContext实例），然后持久化到session中。在filter原路返回时，还需要保存这个SecurityContext实例到session中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;RequestCacheAwareFilter
  用于用户登录成功后，重新恢复因为登录被打断的请求&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;AnonymousAuthenticationFilter
  如果之前的过滤器都没有认证成功，则为当前的SecurityContext中添加一个经过匿名认证的token, 所有与认证相关的过滤器（如CasAuthenticationFilter）都应当放在AnonymousAuthenticationFilter之前。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SessionManagementFilter
  1.session固化保护-通过session-fixation-protection配置
  2.session并发控制-通过concurrency-control配置&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;ExceptionTranslationFilter
  主要拦截两类安全异常：认证异常、访问拒绝异常。而且仅仅是捕获后面的过滤器产生的异常。所以在自定义拦截器时，需要注意在链中的顺序。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;FilterSecurityInterceptor
  通过决策管理器、认证管理器、安全元数据来判断用户是否能够访问资源。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;filtersecurityinterceptor&quot;&gt;FilterSecurityInterceptor&lt;/h1&gt;

&lt;p&gt;如果一个http请求能够匹配security定义的规则，那么该请求将进入security处理流程，大体上，security分为三个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AuthenticationManager 处理认证请求&lt;/li&gt;
  &lt;li&gt;AccessDecisionManager 提供访问决策&lt;/li&gt;
  &lt;li&gt;SecurityMetadataSource 元数据&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下代码摘自&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbstractSecurityInterceptor&lt;/code&gt;， 这是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilterSecurityInterceptor&lt;/code&gt;的父类， 也正是在此处区分了web请求拦截器与方法调用拦截器。(代码有所精简)&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterceptorStatusToken&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;beforeInvocation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getSecureObjectClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConfigAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
	        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;obtainSecurityMetadataSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAttributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rejectPublicInvocations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;publishEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PublicInvocationEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// no further work post-invocation&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SecurityContextHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAuthentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;c1&quot;&gt;//...&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;nc&quot;&gt;Authentication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authenticated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authenticateIfRequired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Attempt authorization&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accessDecisionManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;decide&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AccessDeniedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessDeniedException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;publishEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AuthorizationFailureEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
		            &lt;span class=&quot;n&quot;&gt;authenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accessDeniedException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessDeniedException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Authentication&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authenticateIfRequired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Authentication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityContextHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAuthentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAuthenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alwaysReauthenticate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authenticationManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;SecurityContextHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAuthentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在FilterSecurityInterceptor的处理流程中，首先会处理认证请求，获取用户信息，然后决策处理器根据用户信息与权限元数据进行决策，同样，这三个部分都是可以自定义的。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 自定义过滤器 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;filterSecurityInterceptor&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.security.web.access.intercept.FilterSecurityInterceptor&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;securityMetadataSource&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;securityMetadataSource&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;authenticationManager&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;authenticationManager&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;accessDecisionManager&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;accessDecisionManager&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;authenticationmanager&quot;&gt;AuthenticationManager&lt;/h1&gt;

&lt;p&gt;AuthenticationManager处理认证请求，然而它并不直接处理，而是将工作委托给了一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProviderManager&lt;/code&gt;，ProviderManager又将工作委托给了一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AuthenticationProvider&lt;/code&gt;列表，只要任何一个AuthenticationProvider认证通过，则AuthenticationManager认证通过，我们可以配置一个或者多个AuthenticationProvider，还可以对密码进行加密。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;security:authentication-manager&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;authenticationManager&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;security:authentication-provider&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;user-service-ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;userDetailsService&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;security:password-encoder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;base64=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hash=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;md5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;security:salt-source&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;user-property=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/security:password-encoder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/security:authentication-provider&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/security:authentication-manager&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;考虑到一种常见情形，用户输入用户名密码，然后与数据比对，验证用户信息，security提供了类来处理。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;userDetailsService&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dataSource&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dataSource&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;JdbcDaoImpl使用内置的SQL查询数据，这些SQL以常量的形式出现在JdbcDaoImpl开头，同样可以注入修改。&lt;/p&gt;

&lt;h1 id=&quot;accessdecisionmanager&quot;&gt;AccessDecisionManager&lt;/h1&gt;

&lt;p&gt;AccessDecisionManager提供访问决策，它同样不会直接处理，而是仅仅抽象为一种投票规则，然后决策行为委托给所有投票人。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 决策管理器 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;accessDecisionManager&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.security.access.vote.AffirmativeBased&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;allowIfAllAbstainDecisions&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;index=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;list&amp;gt;&lt;/span&gt;
           &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;bean class=&quot;org.springframework.security.web.access.expression.WebExpressionVoter&quot;/&amp;gt;--&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.security.access.vote.RoleVoter&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 支持所有角色名称，无需前缀 --&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rolePrefix&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.security.access.vote.AuthenticatedVoter&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/constructor-arg&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;security提供了三种投票规则：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AffirmativeBased 只要有一个voter同意就通过&lt;/li&gt;
  &lt;li&gt;ConsensusBased 只要投同意票的大于投反对票的就通过&lt;/li&gt;
  &lt;li&gt;UnanimousBased 需要一致同意才通过&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AffirmativeBased&lt;/code&gt;决策过程&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decide&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Authentication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConfigAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configAttributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AccessDeniedException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deny&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AccessDecisionVoter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getDecisionVoters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configAttributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AccessDecisionVoter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACCESS_GRANTED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AccessDecisionVoter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACCESS_DENIED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;deny&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++;&lt;/span&gt;

			&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deny&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccessDeniedException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;s&quot;&gt;&quot;AbstractAccessDecisionManager.accessDenied&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Access is denied&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// To get this far, every AccessDecisionVoter abstained&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;checkAllowIfAllAbstainDecisions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;securitymetadatasource&quot;&gt;SecurityMetadataSource&lt;/h1&gt;

&lt;p&gt;SecurityMetadataSource定义权限元数据（如资源与角色的关系），并提供了一个核心方法&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collection&amp;lt;ConfigAttribute&amp;gt; getAttributes(Object object)&lt;/code&gt;来获取资源对应的角色列表，这种结构非常类似于Map。&lt;/p&gt;

&lt;p&gt;security提供了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DefaultFilterInvocationSecurityMetadataSource&lt;/code&gt;来进行角色读取操作，并将数据存储委托给一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LinkedHashMap&lt;/code&gt;对象。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 资源与角色关系元数据 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;securityMetadataSource&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;index=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;top.rainynight.site.core.RequestMapFactoryBean&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dataSource&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dataSource&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/constructor-arg&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;DefaultFilterInvocationSecurityMetadataSource获取角色方法&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConfigAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getAttributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FilterInvocation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RequestMatcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConfigAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestMap&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;entrySet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Thu, 12 Mar 2015 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/web/spring-security/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/web/spring-security/</guid>
        
        <category>spring</category>
        
        <category>security</category>
        
        <category>Java</category>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>scheme 解释器</title>
        <description>&lt;p&gt;前段时间针对 &lt;a href=&quot;http://baike.baidu.com/link?url=wgd84RHmek_qWtVHP9uhUL97pPelbW1iiUjF39rRuIrSHeG5ekDywMoiyWXDFgkaz3sdkYS2TRXs29CzMa7paa&quot;&gt;scheme&lt;/a&gt; 语言写了一个解释器，现在就 fork 一下当时想法，整理一下其中的脉络，做一个思维快照，以期下次用C语言来实现时可以顺利地进行。
成品在此：&lt;a href=&quot;https://github.com/dubuyuye/scheme-bootstrap&quot;&gt;scheme-bootstrap&lt;/a&gt;。&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#词法作用域&quot; id=&quot;markdown-toc-词法作用域&quot;&gt;词法作用域&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#抽象语法树&quot; id=&quot;markdown-toc-抽象语法树&quot;&gt;抽象语法树&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#语法&quot; id=&quot;markdown-toc-语法&quot;&gt;语法&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#基本操作和值&quot; id=&quot;markdown-toc-基本操作和值&quot;&gt;基本操作和值&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#语法糖&quot; id=&quot;markdown-toc-语法糖&quot;&gt;语法糖&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#letletletrec&quot; id=&quot;markdown-toc-letletletrec&quot;&gt;let,let*,letrec&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#内部定义&quot; id=&quot;markdown-toc-内部定义&quot;&gt;内部定义&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#惰性求值&quot; id=&quot;markdown-toc-惰性求值&quot;&gt;惰性求值&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#尾调用优化&quot; id=&quot;markdown-toc-尾调用优化&quot;&gt;尾调用优化&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#垃圾回收&quot; id=&quot;markdown-toc-垃圾回收&quot;&gt;垃圾回收&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;词法作用域&quot;&gt;词法作用域&lt;/h1&gt;

&lt;p&gt;一条语句，在不同的函数当中可能具备不同的含义，其中变量的值取决于具体的作用域，所以对一条语句进行解释时需要考虑当时的上下文。
然而lisp是一种函数式的编程语言，函数可以被当成数据来进行传递，这会产生一个问题，如果函数体中存在自由变量，那么这个函数的行为将产生不可控的变化，
问题重点不是变化，而是不可控，在语言的使用者不需要函数行为变化的时候，自由变量会从外部作用域取值，自动产生行为的变化。
这种作用域叫做动态作用域 （Dynamic scope ）。&lt;/p&gt;

&lt;p&gt;像java，javascript之类的语言，都是在对象内部维护一个成员变量，函数中自由的局部变量根据这个成员变量的值来确定行为，虽然函数的行为都可变，但它是可控的。
为了实现可控，我们需要让语言满足人的直觉，即作用域和手写的代码结构相同，即使一个函数会被传递，其内部变量的值也应当追溯到函数定义时的作用域中。
这种作用域就叫做词法作用域 （Lexical scope）。&lt;/p&gt;

&lt;p&gt;显然，实现词法作用域比动态作用域复杂些。实现动态作用域时，函数传递只要传递函数文本，然后插入到调用语句中进行解释就行了；
而词法作用域需要为每个被传递的函数绑定一个定义时的作用域，通常将被传递函数体与定义时的作用域打包成一个数据结构，这个结构叫做&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;闭包&lt;/code&gt;。
javascript就是使用闭包传递函数的。&lt;/p&gt;

&lt;h1 id=&quot;抽象语法树&quot;&gt;抽象语法树&lt;/h1&gt;

&lt;p&gt;语言被写下来之后只是一个文本，其内容是对某种数据结构的形式化描述，在对语言进行解释前，必须先对这个文本进行解析，让其描述的结构出现在内存中，这种结构叫做抽象语法树 （AST）。
这个过程比较乏味，无非是对字符串进行扫描，发现嵌套括号就将其作为树中的一个节点。为了跳过这个过程，我选择用scheme来写解释器，lisp 天生就适合进行这样的表处理。
类似这样用自己来解释自己的行为叫做自举。&lt;/p&gt;

&lt;h1 id=&quot;语法&quot;&gt;语法&lt;/h1&gt;

&lt;p&gt;解释器逐行解释语句，那么碰到不同的语法关键字时应当触发不同的解释行为。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;自求值表达式&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当碰到数字和字符串时，表示这是一个自求值表达式，即写下的文本就是值。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;quote&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;引号表达式的值是去掉引号后的内容，其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;'foo&lt;/code&gt; 等价于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(quote foo)&lt;/code&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;begin&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;s1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;s2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;begin 表达式表示其内容是顺序的多条语句，某些表达式只能支持单一的语句，用begin包裹这些语句后，就能当做一条语句使用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;predicate&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;consequent&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;alternative&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对if的处理是非常容易的，解释器先对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;predicate&lt;/code&gt; 求值，如果值为真则对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consequent&lt;/code&gt; 求值，否则对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alternative&lt;/code&gt; 求值。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;lambda&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;lambda 表达式表示一个函数，对其求值就是将函数体，形参列表，作用域打包。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;define&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;define 表达式表示定义，处理方式是求出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的值然后与 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var&lt;/code&gt; 绑定，放入到当前作用域中。这个过程中隐含了对函数的定义，因为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 可能就是一个lambda。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;set!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set!&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;set! 语句提供了修改变量值的能力，如果没有这个关键字，scheme 就是纯粹的函数式语言，其函数将不会产生任何副作用，函数的行为将不可变。
其处理方式类似于define，区别是一个新增，一个修改。需要注意 define 并不能代替 set!, define 定义的变量只能在某个作用域内屏蔽外部的同名变量；
而set!将会沿着作用域链一直向上寻找匹配的变量名，然后进行修改。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;变量&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对变量求值的过程与 set! 类似，解释器将沿着作用域链一直向上寻找匹配的变量名，然后取出变量的值。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;函数调用&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plus&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;函数调用看似有两种形式，其实本质是一种，对变量 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plus&lt;/code&gt; 的求值和对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda&lt;/code&gt; 的求值都将得到一个闭包，所以函数求值的真实过程是，求参数的值，
将参数传递给闭包，然后求闭包的值。对闭包求值的过程为，扩展作用域，将参数值与对应的形参名绑定并放入作用域，这个过程类似于define，
然后返回闭包中的函数过程体，该过程体为一条或多条语句，每条语句都需要被进行解释，这便产生一个递归的解释过程。&lt;/p&gt;

&lt;h1 id=&quot;基本操作和值&quot;&gt;基本操作和值&lt;/h1&gt;

&lt;p&gt;函数并不能从无到有定义出来，其过程总会使用一些其他的函数，例如加法，那么加法从何而来？事实上，这些非常基本的操作都是无法直接定义出来的，
它们需要从解释器中直接映射的。为了实现方便，以下操作都直接从解释器中映射：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; (其实减乘除可以用加法来实现，但是没这个必要)，&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eq?&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;car&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cdr&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cons&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;scheme中函数与数据的界限非常模糊，car cdr cons等看似基本的操作其实可以用函数来实现。&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;基本值&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;语法糖&quot;&gt;语法糖&lt;/h1&gt;

&lt;p&gt;语法糖并非增加什么新功能，而是让某些以有的功能写起来更舒服。对语法糖的处理也比较简单，就是将其结构变化成解释器能够认识的形式，然后发送给解释器重新解释。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;define&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;该表示法为函数定义的语法糖，用基本的define定义函数时，每次都要写出lambda，比较繁琐。
该表示法等价于如下形式&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;cond&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;p1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;e1&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;p2&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;e2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;cond 表达式类似于常见的switch结构，但是每个case自带break，所以无法进入多个case。cond 可以转换成嵌套的if，然后将转换后的表达式转发给解释函数重新解释。&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;p1&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;e1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;p2&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;e2&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;短路的逻辑与可以用嵌套的if来实现&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c2&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;or&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;短路的逻辑或也可以用嵌套的if来实现&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c1&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c2&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;letletletrec&quot;&gt;let,let*,letrec&lt;/h2&gt;

&lt;p&gt;这三个语法糖提供了三种不同的方式来定义作用域，这三种表示法的作用各不相同，它们都建立在lambda的基础上。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;let&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp1&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var2&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;let 表达式提供了定义一个作用域并绑定&lt;strong&gt;互斥&lt;/strong&gt;变量的功能，var1 与 var2 在语义上没有先后之分，也不能相互访问。
let 表达式等价于如下如下形式，这是一个普通的函数调用。&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;var2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;&amp;lt;exp1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;&amp;lt;exp2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;let*&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp1&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var2&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;let* 表达式提供了定义一个作用域并&lt;strong&gt;先后&lt;/strong&gt;绑定变量的功能，var1 与 var2 在语义上存在先后之分，var2 可以访问 var1，而 var1 不能访问 var2。
let* 表达式等价于如下形式&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp1&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var2&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;letrec&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;letrec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp1&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var2&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp2&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;letrec 表达式提供了定义一个作用域并&lt;strong&gt;同时&lt;/strong&gt;绑定变量的功能，var1 与 var2 在语义上为同时定义，var2 可以访问 var1，且 var1 可以访问 var2，
letrec 的存在意义在于屏蔽外部同名变量，假定当前作用域外部存在一个变量 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var2&lt;/code&gt;,那么let和let* 中的var1求值时如果需要访问&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var2&lt;/code&gt;，那么将会访问这个外部的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var2&lt;/code&gt;,
而letrec不同，如果letrec的var1求值是需要访问var2,那么这个var2的值&lt;strong&gt;同一作用域内&lt;/strong&gt;的那个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var2&lt;/code&gt;的值。
letrec 表达式等价于如下形式&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var1&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;**undefined**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;var2&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;**undefined**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set!&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp1&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set!&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;exp2&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解释器在对变量求值时会检查变量的值，如果其值为一个未定义标记，则会提示未定义错误。&lt;/p&gt;

&lt;h1 id=&quot;内部定义&quot;&gt;内部定义&lt;/h1&gt;

&lt;p&gt;函数的内部可以嵌套地使用define语句，但是define在写成文本时是存在先后的，但是函数内部定义的语义应当是同时定义，所以在对lambda进行解释时需要一些调整，
调整内容如下，&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;扫描出lambda体内的所有define语句，只扫描内部定义的第一层无需嵌套，然后将define的变量名和值表达式(无需求值)装配成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;letrec&lt;/code&gt;的kv绑定形式；&lt;/li&gt;
  &lt;li&gt;扫描出lambda内部的所有非定义语句将这个序列作为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;letrec&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;用上面得到的两个部分组成一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;letrec&lt;/code&gt;；&lt;/li&gt;
  &lt;li&gt;用新得到的letrec作为body构造一个新的 lambda 来替换原来的lambda。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上文描述的含义为：&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;expa&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;expb&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;等价于如下形式&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;letrec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;expa&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;expb&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;惰性求值&quot;&gt;惰性求值&lt;/h1&gt;

&lt;p&gt;为了实现惰性求值，需要提供一种延迟计算一个表达式的能力，实现方式有两种，一种是将解释器改造成惰性求值解释器，
另一种是用一对关键字 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delay&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;force&lt;/code&gt; 提供局部的惰性和强制求值能力。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;惰性求值解释器&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对任何一个表达式求值时，都不直接求值表达式而是创建一个代理的数据结构来包裹表达式和求值时的环境，这个代理可以作为该表达式值的许诺进行传递。
当需要使用表达式的值时，再对这个代理进求值，解释器并不会完整地求出整个表达式的值，其实这个值依旧是一个代理，解释器很懒，最多只工作到刚刚满足需求的程度。&lt;/p&gt;

&lt;p&gt;理论上来讲，惰性求值的效率应当高过普通的求值器，但是它有个缺陷，惰性求值与赋值语句配合使用时经常会出现意料之外的情况，因此 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haskell&lt;/code&gt; 这种惰性求值的语言同时也是纯函数式的语言。&lt;/p&gt;

&lt;p&gt;因为赋值产生问题例子如下，其函数的返回值并不会发生任何变化，因为赋值表达式被惰性了，并没有真正执行。
要解决这个问题，必须提供一个机制让语言使用者主动地强制执行该表达式。&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set!&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;delay/force&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;用这种方式提供惰性功能并没什么理论上的变化，只是让普通的解释器增加两个case, 在碰到delay时生成代理，碰到force时强制执行。其优点是简单易用，同时缺点也非常明显，
必须为高阶函数提供两个版本，一个版本普通，一个版本惰性，而且在使用惰性功能时，代码中会遍布这两个关键字。&lt;/p&gt;

&lt;p&gt;在我写的解释器中用&lt;strong&gt;delay/force&lt;/strong&gt;的方式提供了惰性求值的功能。&lt;/p&gt;

&lt;h1 id=&quot;尾调用优化&quot;&gt;尾调用优化&lt;/h1&gt;

&lt;p&gt;scheme语言没有循环，循环的本质就是在递归时不断对一个变量进行读写，这个变量作为终止条件便可以控制递归的次数，在没有循环时，可以用在递归函数中传递这个变化的因子来代替。
这种递归比较特殊，它在栈中只占一帧，并不会在栈空间累积数据，要使递归能够代替循环，必须进行尾调用优化。&lt;/p&gt;

&lt;p&gt;因为我的解释器是自举的，scheme语言本身提供了对尾调用的优化，在对递归的函数进行解释时，保存当前一帧中的重要数据的负担被转嫁到了解释器中，
如果编写解释器的语言恰好也支持尾调用优化，这一负担会消弭于无形。但是如果编写解释器的语言没有尾调用优化呢？那我们就需要自己处理这种优化。&lt;/p&gt;

&lt;p&gt;处理尾调用优化比较复杂，需要将解释器写成寄存器风格，用栈来保存寄存器中的历史值。
在解释一个表达式前，将寄存器值入栈，解释一个表达式后，栈中内容弹出到寄存器中，解释一个表达式的值不会在栈中留下任何数据。
如果表达式中存在子表达式，那么这个过程也适用于子表达式，显而易见，子表达式解释前的数据入栈时将压在父表达式的数据之上。
如果子表示的嵌套层数太深，数据将有可能撑满整个栈，递归函数通常极有可能嵌套太深。&lt;/p&gt;

&lt;p&gt;基于这种解释器模型，可以选择让表达式序列中的最后一条语句在解释之前数据不入栈，这最后一条语句执行完毕后，直接修改寄存器，数据也不必出栈。
如果最后一条语句仅仅为一个函数调用，那么这个语句将不会在栈中累积数据，即使这个函数是递归的也同样如此。&lt;/p&gt;

&lt;h1 id=&quot;垃圾回收&quot;&gt;垃圾回收&lt;/h1&gt;

&lt;p&gt;垃圾回收的目标是函数执行时扩展的作用域以及函数的闭包，判别一段内存是否可回收的方式是枚举根节点。
从寄存器中的指针出发，经过一系列car,cdr能达到的对象，有可能影响未来的计算过程，那些不能达到的都是可回收的垃圾。&lt;/p&gt;

&lt;p&gt;如果采用&lt;strong&gt;停止并复制&lt;/strong&gt;的算法进行回收，其流程大致如下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;GC开始之前，将所有寄存器内容保存到一个预先分配好的表里，并让root寄存器指向这个表的顶部序对。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;GC时，先从root表开始，一个一个地复制序对，原内存中被复制的序对，car标记为“破碎的心”，cdr放置一个前向指针，指向序对复制后的新位置。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;状态控制，free，scan。
free 指向下一段可用内存，内存分配时递增的地址就是通过它来实现，通过他可以知道当前内存的使用状态。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;GC循环，scan初始指向一个新内存区的对象A，而该对象car,cdr仍指向旧内存区的对象，假定scan正在扫描A对象的car，此时需要检查car指向的对象是否已被复制。
如果未复制就将其复制到free所指的位置并更新free，同时在旧对象做标记，然后更新car使其指向复制后的对象。如果car指向的对象已被复制，则car更新为将该旧对象的cdr中的前向指针。
当scan超过free时GC结束。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 21 Jul 2014 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/lisp/the-scheme-eval/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/lisp/the-scheme-eval/</guid>
        
        <category>lisp</category>
        
        <category>scheme</category>
        
        <category>eval</category>
        
        
        <category>lisp</category>
        
      </item>
    
      <item>
        <title>设计模式与原则</title>
        <description>&lt;p&gt;设计模式的定义：在某情境下，针对某问题的某种解决方案。但是满足此定义的方案并不一定是设计模式，设计模式要求解决方案必须是可复用的。
设计模式的作用大体上是：优化结构，消除依赖，将面向过程转为面向对象。按照功能，一般可以将设计模式分为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;创建型&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;行为型&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;结构型&lt;/code&gt;三大类。
本文将列举这些设计模式，并对每个设计模式进行简要描述，描述格式为：名称，定义，案例，适用性，结构，效果，应用，相关。&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#创建型&quot; id=&quot;markdown-toc-创建型&quot;&gt;创建型&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#单例模式&quot; id=&quot;markdown-toc-单例模式&quot;&gt;单例模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#工厂模式&quot; id=&quot;markdown-toc-工厂模式&quot;&gt;工厂模式&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#行为型&quot; id=&quot;markdown-toc-行为型&quot;&gt;行为型&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#策略模式&quot; id=&quot;markdown-toc-策略模式&quot;&gt;策略模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#模板方法&quot; id=&quot;markdown-toc-模板方法&quot;&gt;模板方法&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#状态模式&quot; id=&quot;markdown-toc-状态模式&quot;&gt;状态模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#命令模式&quot; id=&quot;markdown-toc-命令模式&quot;&gt;命令模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#观察者模式&quot; id=&quot;markdown-toc-观察者模式&quot;&gt;观察者模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#迭代器模式&quot; id=&quot;markdown-toc-迭代器模式&quot;&gt;迭代器模式&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#结构型&quot; id=&quot;markdown-toc-结构型&quot;&gt;结构型&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#装饰者模式&quot; id=&quot;markdown-toc-装饰者模式&quot;&gt;装饰者模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#适配器模式&quot; id=&quot;markdown-toc-适配器模式&quot;&gt;适配器模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#代理模式&quot; id=&quot;markdown-toc-代理模式&quot;&gt;代理模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#外观模式&quot; id=&quot;markdown-toc-外观模式&quot;&gt;外观模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#组合模式&quot; id=&quot;markdown-toc-组合模式&quot;&gt;组合模式&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;设计模式是工程师们从工作中总结出来的经验之谈，这些经验除了设计模式，还有一些设计原则，严格来讲，这些东西都是教条，它告诉我们只要按照规矩来，就不容易犯错。当遇到一特殊情况时，打破原则也没什么大不了的。&lt;/p&gt;

&lt;p&gt;这些原则包括：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;单一责任原则：一个类应该只有一个引起变化的原因。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;里氏替换原则：子类可以扩展父类的功能，但不能改变父类原有的功能。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;依赖倒置原则：高层模块不应该依赖低层模块，二者都应该依赖其抽象；抽象不应该依赖细节；细节应该依赖抽象。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;接口隔离原则：客户端不应该依赖它不需要的接口；一个类对另一个类的依赖应该建立在最小的接口上。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;最少知识原则：一个对象应该对其他对象保持最少的了解。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;开闭原则：一个软件实体如类、模块和函数应该对扩展开放，对修改关闭。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;以及一些叫不上名的原则：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;找出应用中可能需要变化之处，把他们独立出来，不要和那些不需要变化的代码混在一起。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;针对接口编程，而不是针对实现编程。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;多用组合少用继承。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;创建型&quot;&gt;创建型&lt;/h1&gt;

&lt;h2 id=&quot;单例模式&quot;&gt;单例模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;确保一个类只有一个实例，并提供一个全局访问点。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;需要对系统的注册表进行操作，如果同时存在多个注册表对象的话，将无法对并发访问或者临界值进行控制。&lt;/p&gt;

&lt;p&gt;建立一个注册表获取类，从其静态方法中获取注册表对象，单例采用双重检查法创建。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当对象的操作目标是当前环境中的唯一资源时&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201204/26/1335439688_4086.jpg&quot; alt=&quot;Singleton&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;将可以进行并发访问控制&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Runtime&lt;/p&gt;

&lt;p&gt;NumberFormat&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;简单工厂&lt;/p&gt;

&lt;h2 id=&quot;工厂模式&quot;&gt;工厂模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;分类&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;工厂方法，抽象工厂，（简单工厂算工厂方法的特例）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;工厂方法：定义了一个创建对象的接口，但由子类决定需要实例化的类是哪一个。工厂方法将类的实例化推迟到子类。&lt;/p&gt;

&lt;p&gt;抽象工厂：定义一个接口，用于创建相关或依赖对象的家族，而不用明确指定具体的类。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;现有系统需要获取一组批萨饼的调料，但是同样的调料在不同的地点会有不同，如海边的海鲜是新鲜的，而内地的海鲜是冷冻的，为了方便扩展，获取的调料不能依赖具体的地点。&lt;/p&gt;

&lt;p&gt;解决方案：将获取调料的地点抽象，由运行时的具体地点生成一组调料。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当需要依赖运行时上下文来决定生成的产品家族时使用抽象工厂&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;区别&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;工厂方法：一个抽象产品，可以派生出多种具体产品；一个抽象工厂类，可以派生出多个具体工厂。&lt;/p&gt;

&lt;p&gt;抽象工厂：多个抽象产品类，每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类，可以派生出多个具体工厂类。每个具体工厂类可以创建多个具体产品类的实例。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;工厂方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://hi.csdn.net/attachment/201203/15/0_1331817716F3IJ.gif&quot; alt=&quot;fatocymethod&quot; /&gt;&lt;/p&gt;

&lt;p&gt;抽象工厂&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201204/25/1335357105_2682.jpg&quot; alt=&quot;absfactory&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;能轻松方便地构造对象实例，而不必关心构造对象实例的细节和依赖条件。&lt;/p&gt;

&lt;p&gt;类数量暴增&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;基本装箱类&lt;/p&gt;

&lt;p&gt;Collection的iterator()&lt;/p&gt;

&lt;p&gt;log4j&lt;/p&gt;

&lt;h1 id=&quot;行为型&quot;&gt;行为型&lt;/h1&gt;

&lt;h2 id=&quot;策略模式&quot;&gt;策略模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;定义了算法家族，分别封装起来，让他们之间可以相互替换，此模式让算法的变化独立于使用算法的客户。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;存在一个模拟鸭子的程序，现在需要为其增加飞翔的功能，在鸭子的抽象类上增加&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fly()&lt;/code&gt;方法后，飞翔能力将传播到所有的子类中。但是，本不应该会飞的 “模型鸭” 也继承了飞翔能力，如果将飞翔能力抽象为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flayable&lt;/code&gt;接口，那么随着程序的扩展，那么所有子类都必须重新实现fly()方法，如此一来将出现大量的重复代码，如果已经现存很多扩展类，修改这些类也是很大的工作量。&lt;/p&gt;

&lt;p&gt;此时的解决方案是：将飞翔行为抽象，并提供几个通用实现，将鸭子的飞翔委托给飞翔行为，在抽象类中增加set方法注册委托，这样的改动不会影响现有的结构。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当类的某个行为随着扩展不断发生变化，而且这种变化只有有限的几种时。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/11/1336732187_4598.jpg&quot; alt=&quot;Strategy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;积极：&lt;/p&gt;

&lt;p&gt;1.类与行为可以分别扩展，而且扩展类可以自由设置行为，甚至在运行时改变。&lt;/p&gt;

&lt;p&gt;2.类预置了多个算法，需要要判断调用时上下文从而选择不同算法。策略模式可以消除代码中的 if lese 将选择权交给客户&lt;/p&gt;

&lt;p&gt;消极:&lt;/p&gt;

&lt;p&gt;1.客户端必须知道所有的策略类，并自行决定使用哪一个策略类
2.策略模式将造成产生很多策略类，可以通过使用享元模式在一定程度上减少对象的数量。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThreadPoolExecutor&lt;/code&gt;中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RejectedExecutionHandler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SpringSecurity&lt;/code&gt;中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccessDecisionManager&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;模板方法模式&lt;/p&gt;

&lt;p&gt;命令模式&lt;/p&gt;

&lt;p&gt;状态模式&lt;/p&gt;

&lt;h2 id=&quot;模板方法&quot;&gt;模板方法&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在一个方法中一定一个算法的骨架，而将某些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下，重新定义算法中的某些步骤。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;现有一个冲泡饮料的算法，泡咖啡和泡茶的步骤基本相同，但是在加调料的这一步有所区别，如何在不需要重写整个冲泡过程的前提下扩展？&lt;/p&gt;

&lt;p&gt;解决方案：将冲泡过程中的放调料步骤抽象出来做成hook，待具体的实现者来补全算法，而不必重写整个冲泡过程。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当算法的整体骨架可被复用，只有其中某个步骤可变时&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/14/1336965093_1048.jpg&quot; alt=&quot;templatemethod&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;复用算法骨架&lt;/p&gt;

&lt;p&gt;算法可变步骤中的每一个特例都要为其创建一个新类&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.sort&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;与策略模式不同，策略模式定义一个算法家族，算法可以互换，而模板方法定义一个算法大纲，其中个别的步骤可以有不同实现。&lt;/p&gt;

&lt;h2 id=&quot;状态模式&quot;&gt;状态模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;允许对象内部状态改变时改变它的行为，对象看起来好像修改了它的类。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;需要模拟一个自动售货机程序，如果没有投币则只能投币；如果已投币则不能再投币，可以选择退币或者出货；选择出货后如果有货则出货并结束，如果断货则提示断货。&lt;/p&gt;

&lt;p&gt;可以用大量的 if else 将如上描述写成过程话的判断语句以完成功能，然而现在需要增加一个功能，有十分之一的几率双倍出货。这样一来就需要在每个动作方法内判断当前操作者是不是幸运儿。&lt;/p&gt;

&lt;p&gt;解决方案：将状态封装成独立的类，并将动作委托到代表当前状态的对象。省去了类中冗长的状态判断。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;行为因为上下文的不同而需要随之变化&lt;/p&gt;

&lt;p&gt;代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件（if else(或switch case)语句，且这些分支依赖于该对象的状态。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/11/1336719144_5496.jpg&quot; alt=&quot;state&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;它将与特定状态相关的行为局部化，并且将不同状态的行为分割开来&lt;/p&gt;

&lt;p&gt;它使得状态转换显式化&lt;/p&gt;

&lt;p&gt;状态对象可被共享&lt;/p&gt;

&lt;p&gt;状态模式的使用必然会增加系统类和对象的个数。&lt;/p&gt;

&lt;p&gt;状态模式的结构与实现都较为复杂，如果使用不当将导致程序结构和代码的混乱。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.util.Iterator&lt;/p&gt;

&lt;p&gt;动作游戏连招&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;是策略模式的增强版，区别在于意图，策略模式虽然也可以在运行时改变行为，但是策略模式通常有一个最适合的策略对象；而状态模式需要在多个状态对象中游走，没有所谓的最适合状态。&lt;/p&gt;

&lt;h2 id=&quot;命令模式&quot;&gt;命令模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;将请求封装成对象，以便使用不同请求，队列或日志来参数化其他对象，命令模式也支持可撤销的操作。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;通用遥控器，当目标为电视时，上下键可以是换台，调整音量；当目标为空调时，上下键可以是调温或者调整定时；而且可以方便地扩展以兼容不同电器。&lt;/p&gt;

&lt;p&gt;解决方案，将遥控器与电器接偶，将遥控器每个按钮抽象为一个命令，由使用者提供命令，遥控器呼叫此命令，由命令自身呼叫真实目标。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当请求的真实目标不明确，或者对请求进行不明确的处理时(如队列，宏命令，日志记录等)。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/09/1336547877_9980.jpg&quot; alt=&quot;cmd&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;积极的：&lt;/p&gt;

&lt;p&gt;降低系统的耦合度:Command模式将调用操作的对象与知道如何实现该操作的对象解耦。&lt;/p&gt;

&lt;p&gt;组合命令:你可将多个命令装配成一个组合命令，即可以比较容易地设计一个命令队列和宏命令。一般说来，组合命令是Composite模式的一个实例。&lt;/p&gt;

&lt;p&gt;增加新的Command很容易，因为这无需改变已有的类。&lt;/p&gt;

&lt;p&gt;可以方便地实现对请求的Undo和Redo。用栈存储操作序列，可以实现多层次撤销。&lt;/p&gt;

&lt;p&gt;消极的：&lt;/p&gt;

&lt;p&gt;导致某些系统有过多的具体命令类&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.lang.Runnable&lt;/p&gt;

&lt;p&gt;javax.swing.Action&lt;/p&gt;

&lt;p&gt;游戏中的自定义键位&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;策略模式聚焦的是对相同请求更换解决方案的灵活性；而命令模式聚焦的是对多请求变化的封装以及对相同请求不同的请求形式解决方法的可复用性&lt;/p&gt;

&lt;p&gt;组合模式&lt;/p&gt;

&lt;h2 id=&quot;观察者模式&quot;&gt;观察者模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;定义了对象之间的一对多依赖，这样一来，当一个对象改变状态时，所有所有依赖者都会接到通知并自动更新&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;需要在展板上实时显示当前温度，温度数据来自温度计。初步方案1,是定时询问温度计然后更新数据，这样就需要再开一个定时器线程，如果以后会有更多的功能需要获取温度计数据，那么线程数量将失去控制。数步方案2是当温度计数据发生变化时，直接通知这些数据请求者，但是当有新的请求者时，需要打开温度计类增加一个请求者。&lt;/p&gt;

&lt;p&gt;结局方案：将需要获取温度计数据的请求者抽象，然后注册到温度计中形成一个列表，每当温度计数据发生变化，就依次通知这些注册的对象。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当一个抽象模型有两个方面, 其中一个方面依赖于另一方面的状态。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。&lt;/p&gt;

&lt;p&gt;当对一个对象的改变需要同时改变其它对象 , 而不知道具体有多少对象有待改变。&lt;/p&gt;

&lt;p&gt;当一个对象必须通知其它对象，而它又不能假定其它对象是谁。换言之 , 你不希望这些对象是紧密耦合的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/11/1336707179_9037.jpg&quot; alt=&quot;Observer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Observer模式允许你独立的改变目标和观察者&lt;/p&gt;

&lt;p&gt;观察者模式可以实现表示层和数据逻辑层的分离&lt;/p&gt;

&lt;p&gt;支持广播通信&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.util.EventListener&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;终结者模式&lt;/p&gt;

&lt;p&gt;单例模式&lt;/p&gt;

&lt;h2 id=&quot;迭代器模式&quot;&gt;迭代器模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;提供一种方法顺序访问聚合对象中的每个元素，而又不暴露其内部表示。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/28/1338213169_8415.jpg&quot; alt=&quot;Iterator&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;封装性良好，用户只需要得到迭代器就可以遍历，而对于遍历算法则不用去关心。&lt;/p&gt;

&lt;p&gt;增加新的聚合类需要对应增加新的迭代器类，类的个数成对增加&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.util.Iterator
java.util.Enumeration&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;组合模式，常用来便利组合模式的对象图&lt;/p&gt;

&lt;p&gt;备忘录模式&lt;/p&gt;

&lt;p&gt;工厂方法模式，迭代器对象由实现类提供&lt;/p&gt;

&lt;h1 id=&quot;结构型&quot;&gt;结构型&lt;/h1&gt;

&lt;h2 id=&quot;装饰者模式&quot;&gt;装饰者模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;动态地将职责附加到对象上，若要扩展功能，装饰者提供了比继承更有弹性的替代方案。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;模拟一个冲泡咖啡的程序，调料种类未知，而且以后可能增减，每种调料价格不同，客户可自行选择调料份量，现在需要计算该咖啡的价格。初步方案是列出所有可能的组合，然后计算组合的价格，但是如果调料种类较多，那么组合将会爆炸，考虑到调料的份量有无限种可能，列举组合方式不可行。&lt;/p&gt;

&lt;p&gt;解决方案：将咖啡抽象，将每种调料对应一种咖啡，如加奶的咖啡，加摩卡的咖啡等，这些调料的咖啡成分由客户自己提供。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当需要对一个对象以可选的方式增强功能时&lt;/p&gt;

&lt;p&gt;当无法使用继承的方式增强功能时&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/03/1336034007_4657.jpg&quot; alt=&quot;decorater&quot; /&gt;&lt;/p&gt;

&lt;p&gt;抽象组件角色(Component)：定义一个对象接口，以规范准备接受附加责任的对象，&lt;/p&gt;

&lt;p&gt;即可以给这些对象动态地添加职责。&lt;/p&gt;

&lt;p&gt;具体组件角色(ConcreteComponent) :被装饰者，定义一个将要被装饰增加功能的类。&lt;/p&gt;

&lt;p&gt;可以给这个类的对象添加一些职责&lt;/p&gt;

&lt;p&gt;抽象装饰器(Decorator):维持一个指向构件Component对象的实例，&lt;/p&gt;

&lt;p&gt;并定义一个与抽象组件角色Component接口一致的接口&lt;/p&gt;

&lt;p&gt;具体装饰器角色（ConcreteDecorator):向组件添加职责。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;运行时扩展现有对象，比继承灵活。&lt;/p&gt;

&lt;p&gt;产生很多小类&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.io.BufferedInputStream(InputStream)&lt;/p&gt;

&lt;p&gt;java.io.DataInputStream(InputStream)&lt;/p&gt;

&lt;p&gt;java.io.BufferedOutputStream(OutputStream)&lt;/p&gt;

&lt;p&gt;java.util.zip.ZipOutputStream(OutputStream)&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;java.util.Collections# checkedList&lt;/td&gt;
      &lt;td&gt;Map&lt;/td&gt;
      &lt;td&gt;Set&lt;/td&gt;
      &lt;td&gt;SortedSet&lt;/td&gt;
      &lt;td&gt;SortedMap&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;可与工厂模式和生成器模式配合使用&lt;/p&gt;

&lt;p&gt;Decorator模式不同于Adapter模式，因为装饰仅改变对象的职责而不改变它的接口；而适配器将给对象一个全新的接口。&lt;/p&gt;

&lt;p&gt;Strategy模式：用一个装饰你可以改变对象的外表；而Strategy模式使得你可以改变对象的内核。这是改变对象的两种途径。&lt;/p&gt;

&lt;h2 id=&quot;适配器模式&quot;&gt;适配器模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;将一个类的接口，转换成客户期望的另一个接口，适配器让原本接口不兼容的类可以合作无间。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;插座有插头型号对不上，用适配器抓换下。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;想使用一个已经存在的类，而它的接口不符合需求时。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;类适配器&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/02/1335939433_8937.jpg&quot; alt=&quot;adapter1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对象适配器&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/02/1335939552_3860.jpg&quot; alt=&quot;adapter2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.util.Arrays# asList()&lt;/p&gt;

&lt;p&gt;java.io.InputStreamReader(InputStream)&lt;/p&gt;

&lt;p&gt;java.io.OutputStreamWriter(OutputStream)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;类似装饰者，而者都是包装对象，但是装饰者从不转换接口，只是增加职责，而适配器正是转换接口。&lt;/p&gt;

&lt;p&gt;外观模式&lt;/p&gt;

&lt;h2 id=&quot;代理模式&quot;&gt;代理模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;为另一个对象提供一个替身或占位符以控制对这个对象的访问&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;需要编写一个类来监控远程服务器上的一些数据，但是希望这个类易于使用。结局方案，将远程服务上的数据抽象成一个接口，用RMI进行通信，看起来就好象直接操纵了远程对象。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) 远程代理（Remote  Proxy）为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中，也可是在另一台主机中，远程代理又叫做大使(Ambassador)&lt;/p&gt;

&lt;p&gt;2) 虚拟代理（Virtual Proxy）根据需要创建开销很大的对象。如果需要创建一个资源消耗较大的对象，先创建一个消耗相对较小的对象来表示，真实对象只在需要时才会被真正创建。&lt;/p&gt;

&lt;p&gt;3) 保护代理（Protection Proxy）控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。&lt;/p&gt;

&lt;p&gt;4) 智能指引（Smart Reference）取代了简单的指针，它在访问对象时执行一些附加操作。&lt;/p&gt;

&lt;p&gt;5) Copy-on-Write代理：它是虚拟代理的一种，把复制（克隆）操作延迟到只有在客户端真正需要时才执行。一般来说，对象的深克隆是一个开销较大的操作，Copy-on-Write代理可以让这个操作延迟，只有对象被用到的时候才被克隆。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/07/1336371130_8874.jpg&quot; alt=&quot;proxy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;封装实现细节，只暴露数据抽象&lt;/p&gt;

&lt;p&gt;延迟加载&lt;/p&gt;

&lt;p&gt;权限控制&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.lang.reflect.Proxy&lt;/p&gt;

&lt;p&gt;RMI&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;与装饰者很类似，然而目的不同，装饰者增加行为，代理模式控制访问&lt;/p&gt;

&lt;h2 id=&quot;外观模式&quot;&gt;外观模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;提供了一个统一的接口，用来访问子系统中的一群接口。外观定义了一个高层接口，让子系统更容易使用。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;程序由多个子系统联合起来工作，而且这些子组件需要使用者自己组装，这样使用起来会很复杂，而且客户与子组件耦合较高。&lt;/p&gt;

&lt;p&gt;解决方案，提供一套简化的接口，客户端可以直接操作此接口，而无需理会复杂的系统结构。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当需要为一个复杂子系统提供一个简单接口时。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201207/05/1341473411_7539.JPG&quot; alt=&quot;facade&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;对客户屏蔽子系统组件，减少了客户处理的对象数目并使得子系统使用起来更加容易。&lt;/p&gt;

&lt;p&gt;只是提供了一个访问子系统的统一入口，并不影响用户直接使用子系统类。&lt;/p&gt;

&lt;p&gt;不能很好地限制客户使用子系统类&lt;/p&gt;

&lt;p&gt;增加新的子系统可能需要修改外观类或客户端的源代码，违背了“开闭原则”。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;java.lang.Class&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;适配器模式&lt;/p&gt;

&lt;h2 id=&quot;组合模式&quot;&gt;组合模式&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;案例&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;菜单中有很多道菜，而且其中甜点是个子菜单，现在需要打印整个菜单。&lt;/p&gt;

&lt;p&gt;解决方案是：将子菜单与总顶层菜单的接口统一起来，都为Menu类型，大Menu内部维护菜单列表以及子Menu。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;适用性&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;想表示对象的部分-整体层次结构&lt;/p&gt;

&lt;p&gt;希望用户忽略组合对象与单个对象的不同，用户将统一地使用组合结构中的所有对象。&lt;/p&gt;

&lt;p&gt;需要注意的是，虽然通常情况下组合模式以树形结构体现，但其实不是一回事，组合模式拥有更抽象的语义，它是一种整体与部分一致的抽象。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://my.csdn.net/uploads/201205/03/1336015104_5713.jpg&quot; alt=&quot;composive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;定义了包含基本对象和组合对象的类层次结构 基本对象可以被组合成更复杂的组合对象，而这个组合对象又可以被组合，这样不断的递归下去。&lt;/p&gt;

&lt;p&gt;简化客户代码 客户可以一致地使用组合结构和单个对象。&lt;/p&gt;

&lt;p&gt;使得更容易增加新类型的组件&lt;/p&gt;

&lt;p&gt;更难以排除特定类型的组件&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;javax.swing.JComponent# add(Component)&lt;/p&gt;

&lt;p&gt;java.awt.Container# add(Component)&lt;/p&gt;

&lt;p&gt;java.util.Map# putAll(Map)&lt;/p&gt;

&lt;p&gt;java.util.List# addAll(Collection)&lt;/p&gt;

&lt;p&gt;java.util.Set# addAll(Collection)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相关&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;可与迭代器模式搭配使用&lt;/p&gt;

</description>
        <pubDate>Sun, 22 Jun 2014 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/common/design-pattern/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/common/design-pattern/</guid>
        
        <category>design</category>
        
        <category>pattern</category>
        
        <category>设计模式</category>
        
        <category>原则</category>
        
        <category>面向对象</category>
        
        
        <category>common</category>
        
      </item>
    
      <item>
        <title>Java Concurrent</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#线程安全的类&quot; id=&quot;markdown-toc-线程安全的类&quot;&gt;线程安全的类&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#thread&quot; id=&quot;markdown-toc-thread&quot;&gt;Thread&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#interrupt&quot; id=&quot;markdown-toc-interrupt&quot;&gt;interrupt&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#join&quot; id=&quot;markdown-toc-join&quot;&gt;join&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#同步&quot; id=&quot;markdown-toc-同步&quot;&gt;同步&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#锁&quot; id=&quot;markdown-toc-锁&quot;&gt;锁&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#线程安全集合&quot; id=&quot;markdown-toc-线程安全集合&quot;&gt;线程安全集合&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#copyonwrite&quot; id=&quot;markdown-toc-copyonwrite&quot;&gt;CopyOnWrite&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#concurrenthashmap&quot; id=&quot;markdown-toc-concurrenthashmap&quot;&gt;ConcurrentHashMap&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#队列&quot; id=&quot;markdown-toc-队列&quot;&gt;队列&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#弱一致的迭代器&quot; id=&quot;markdown-toc-弱一致的迭代器&quot;&gt;弱一致的迭代器&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#线程池&quot; id=&quot;markdown-toc-线程池&quot;&gt;线程池&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#executor-框架&quot; id=&quot;markdown-toc-executor-框架&quot;&gt;Executor 框架&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#executorservice&quot; id=&quot;markdown-toc-executorservice&quot;&gt;ExecutorService&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#scheduledexecutorservice&quot; id=&quot;markdown-toc-scheduledexecutorservice&quot;&gt;ScheduledExecutorService&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#executors&quot; id=&quot;markdown-toc-executors&quot;&gt;Executors&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#定制-threadpoolexecutor&quot; id=&quot;markdown-toc-定制-threadpoolexecutor&quot;&gt;定制 ThreadPoolExecutor&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#需要特别考虑的问题&quot; id=&quot;markdown-toc-需要特别考虑的问题&quot;&gt;需要特别考虑的问题&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#调整线程池&quot; id=&quot;markdown-toc-调整线程池&quot;&gt;调整线程池&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#future-接口&quot; id=&quot;markdown-toc-future-接口&quot;&gt;Future 接口&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#使用-future-构建缓存&quot; id=&quot;markdown-toc-使用-future-构建缓存&quot;&gt;使用 Future 构建缓存&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#completionservice&quot; id=&quot;markdown-toc-completionservice&quot;&gt;CompletionService&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#forkjoin&quot; id=&quot;markdown-toc-forkjoin&quot;&gt;Fork/Join&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#同步工具&quot; id=&quot;markdown-toc-同步工具&quot;&gt;同步工具&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#semaphore&quot; id=&quot;markdown-toc-semaphore&quot;&gt;Semaphore&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#互斥&quot; id=&quot;markdown-toc-互斥&quot;&gt;互斥&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#cyclicbarrier&quot; id=&quot;markdown-toc-cyclicbarrier&quot;&gt;CyclicBarrier&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#countdownlatch&quot; id=&quot;markdown-toc-countdownlatch&quot;&gt;CountdownLatch&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#exchanger&quot; id=&quot;markdown-toc-exchanger&quot;&gt;Exchanger&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lock工具&quot; id=&quot;markdown-toc-lock工具&quot;&gt;Lock工具&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#reentrantlock&quot; id=&quot;markdown-toc-reentrantlock&quot;&gt;ReentrantLock&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#condition&quot; id=&quot;markdown-toc-condition&quot;&gt;Condition&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#readwritelock&quot; id=&quot;markdown-toc-readwritelock&quot;&gt;ReadWriteLock&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#原子变量&quot; id=&quot;markdown-toc-原子变量&quot;&gt;原子变量&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#并发随机数&quot; id=&quot;markdown-toc-并发随机数&quot;&gt;并发随机数&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#性能与可伸缩性&quot; id=&quot;markdown-toc-性能与可伸缩性&quot;&gt;性能与可伸缩性&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;线程拥有通过程序运行的独立的并发路径，并且每个线程都有自己的程序计数器，称为堆栈和本地变量。线程存在于进程中，它们与同一进程内的其他线程共享内存、文件句柄以及进程状态。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;JDK 5.0 中的并发改进可以分为三组：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;JVM 级别更改。大多数现代处理器对并发对某一硬件级别提供支持，通常以 &lt;strong&gt;compare-and-swap&lt;/strong&gt; （CAS）指令形式。CAS 是一种低级别的、细粒度的技术，它允许多个线程更新一个内存位置，同时能够检测其他线程的冲突并进行恢复。它是许多高性能并发算法的基础。在 JDK 5.0 之前，Java 语言中用于协调线程之间的访问的惟一原语是同步，同步是更重量级和粗粒度的。公开 CAS 可以开发高度可伸缩的并发 Java 类。这些更改主要由 JDK 库类使用，而不是由开发人员使用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;低级实用程序类 – &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;锁定&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;原子类&lt;/code&gt;。使用 CAS 作为并发原语，&lt;strong&gt;ReentrantLock&lt;/strong&gt; 类提供与 synchronized 原语相同的锁定和内存语义，然而这样可以更好地控制锁定（如计时的锁定等待、锁定轮询和可中断的锁定等待）和提供更好的可伸缩性（竞争时的高性能）。大多数开发人员将不再直接使用 ReentrantLock 类，而是使用在 ReentrantLock 类上构建的高级类。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;高级实用程序类。这些类实现并发构建块，每个计算机科学文本中都会讲述这些类 – &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;信号&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;互斥&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;闩锁&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;屏障&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;交换程序&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;线程池&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;线程安全集合类&lt;/code&gt;等。大部分开发人员都可以在应用程序中用这些类来替换许多同步、wait() 和 notify() 的使用，从而提高性能、可读性和正确性。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;线程安全的类&quot;&gt;线程安全的类&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;首先它必须在单线程环境中正确运行。如果正确实现了类，那么说明它符合规范，对该类的对象的任何顺序的操作（公共字段的读写、公共方法的调用）都不应该：&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;使对象处于无效状态；&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;观察处于无效状态的对象；&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;违反类的任何变量、前置条件或后置条件。&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;而且，要成为线程安全的类，在从多个线程访问时，它必须继续正确运行，而不管运行时环境执行那些线程的调度和交叉，且无需对部分调用代码执行任何其他同步。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果没有线程之间的某种明确协调，比如锁定，运行时可以随意在需要时在多线程中交叉操作执行。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在并发编程中，一种被普遍认可的原则就是：尽可能的使用&lt;strong&gt;不可变对象&lt;/strong&gt;来创建简单、可靠的代码。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;thread&quot;&gt;Thread&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;任何一个时刻，对象的控制权（monitor）只能被一个线程拥有。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;无论是执行对象的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notify&lt;/code&gt;还是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifyAll&lt;/code&gt;方法，必须保证当前运行的线程取得了该对象的控制权（monitor）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果在没有控制权的线程里执行对象的以上三种方法，就会报java.lang.IllegalMonitorStateException异常。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;JVM基于多线程，默认情况下不能保证运行时线程的时序性。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;interrupt&quot;&gt;interrupt&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;当调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;th.interrput()&lt;/code&gt;的时候，线程th的中断状态(interrupted status) 会被置位。我们可以通过Thread.currentThread().isInterrupted() 来检查这个布尔型的中断状态。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;没有任何语言方面的需求要求一个被中断的程序应该终止&lt;/strong&gt;。中断一个线程只是为了引起该线程的注意，被中断线程可以决定如何应对中断。这说明interrupt中断的是线程的某一部分业务逻辑，前提是线程需要检查自己的中断状态(isInterrupted())。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当th被阻塞的时候，比如被&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object.wait&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thread.join&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thread.sleep&lt;/code&gt;三种方法之一阻塞时, 调用它的interrput()方法，可想而知，没有占用CPU运行的线程是不可能给自己的中断状态置位的, 这就会产生一个InterruptedException异常。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;join&quot;&gt;join&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;join方法可以让一个线程&lt;strong&gt;等待&lt;/strong&gt;另一个线程执行完成。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;若t是一个正在执行的Thread对象，t.join() 将会使当前线程暂停执行并等待t执行完成。重载的join()方法可以让开发者自定义等待周期。然而，和sleep()方法一样join()方法依赖于操作系统的时间处理机制，你不能假定join()方法将会精确的等待你所定义的时长。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如同sleep()方法，join()方法响应中断并在中断时抛出InterruptedException。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;同步&quot;&gt;同步&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;同步的构造方法没有意义，因为当这个对象被创建的时候，只有创建对象的线程能访问它。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;静态方法是和类（而不是对象）相关的，所以线程会请求类对象(Class Object)的内部锁。因此用来控制类的&lt;strong&gt;静态域&lt;/strong&gt;访问的锁不同于控制&lt;strong&gt;对象&lt;/strong&gt;访问的锁。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果类中的两个域需要同步访问，但是两个域没有什么关联，那么可以为两个域个创建一个私有的锁对象，使两个域能分别同步。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;锁&quot;&gt;锁&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;死锁描述了这样一种情景，两个或多个线程永久阻塞，互相等待对方释放资源。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;饥饿是指当一个线程不能正常的访问共享资源并且不能正常执行的情况。这通常在共享资源被其他“贪心”的线程长期占用时发生&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;一个线程通常会有会响应其他线程的活动。如果其他线程也会响应另一个线程的活动，那么就有可能发生活锁。同死锁一样，发生活锁的线程无法继续执行, 然而线程并没有阻塞, 他们在忙于响应对方无法恢复工作。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;wait方法，释放锁并挂起&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果在一个执行序列中，需要确定对象的状态，那么某个线程在执行这个序列时，需要获得所有这些对象的锁（如一来一回的相互鞠躬，必须保证不会同时鞠躬也不会同时静止）&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;线程安全集合&quot;&gt;线程安全集合&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;java.util.concurrent 包添加了多个新的线程安全集合类 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentHashMap&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyOnWriteArrayList&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyOnWriteArraySet&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;JDK 5.0 还提供了两个新集合接口 – &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Queue&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BlockingQueue&lt;/code&gt;。Queue 接口与 List 类似，但它只允许从后面插入，从前面删除。BlockingQueue定义了一个先进先出的数据结构，当你尝试往满队列中添加元素，或者从空队列中获取元素时，将会阻塞或者超时。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentMap&lt;/code&gt;是java.util.Map的子接口，定义了一些有用的原子操作。移除或者替换键值对的操作只有当key存在时才能进行，而新增操作只有当key不存在时才能进行, 使这些操作原子化，可以避免同步。ConcurrentMap的标准实现是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentHashMap&lt;/code&gt;，它是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashMap&lt;/code&gt;的并发模式。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentNavigableMap&lt;/code&gt;是ConcurrentMap的子接口，支持近似匹配。ConcurrentNavigableMap的标准实现是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentSkipListMap&lt;/code&gt;，它是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TreeMap&lt;/code&gt;的并发模式。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;所有这些集合，通过 在集合里新增对象和访问或移除对象的操作之间，定义一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;happens-before&lt;/code&gt;的关系，来帮助程序员避免内存一致性错误。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;copyonwrite&quot;&gt;CopyOnWrite&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Vector 的常见应用是存储通过组件注册的监听器的列表。当发生适合的事件时，该组件将在监听器的列表中迭代，调用每个监听器。为了防止ConcurrentModificationException，迭代线程必须复制列表或锁定列表，以便进行整体迭代，而这两种情况都需要大量的性能成本。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;CopyOnWriteArrayList及CopyOnWriteArraySet 类通过每次添加或删除元素时创建数组的新副本，避免了这个问题，但是进行中的迭代保持对创建迭代器时的副本进行操作。虽然复制也会有一些成本，但是在许多情况下，迭代要比修改多得多，在这些情况下，写入时复制要比其他备用方法具有更好的性能和并发性。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;concurrenthashmap&quot;&gt;ConcurrentHashMap&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hashtable&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;synchronizedMap&lt;/code&gt; 所采取的获得同步的简单方法（同步 Hashtable 或者同步 Map 封装器对象中的每个方法）有两个主要的不足：&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;第一，这种方法对于可伸缩性是一种障碍，因为一次只能有一个线程可以访问 hash 表；&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;第二，这样仍不足以提供真正的线程安全性，许多公用的混合操作仍然需要额外的同步。虽然诸如 get() 和 put() 之类的简单操作可以在不需要额外同步的情况下安全地完成，但还是有一些公用的操作序列，例如迭代或者 put-if-absent（空则放入），需要外部的同步，以避免数据争用。&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在大多数情况下，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentHashMap&lt;/code&gt; 是 Hashtable或 Collections.synchronizedMap(new HashMap()) 的简单替换。然而，其中有一个显著不同，即 ConcurrentHashMap 实例中的同步&lt;strong&gt;不锁定&lt;/strong&gt;Map进行独占使用, 实际上，没有办法锁定 ConcurrentHashMap 进行独占使用，它被设计用于进行并发访问。为了使集合不被锁定进行独占使用，还提供了公用的混合操作的其他（原子）方法，如 &lt;strong&gt;put-if-absent&lt;/strong&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;ConcurrentHashMap 返回的迭代器是弱一致的，意味着它们将不抛出ConcurrentModificationException ，将进行 “合理操作” 来反映迭代过程中其他线程对 Map 的修改。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;队列&quot;&gt;队列&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Queue&lt;/code&gt; 接口比 List 简单得多，仅包含 put() 和 take() 方法，并允许比 LinkedList 更有效的实现。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Queue 接口还允许实现来确定存储元素的顺序。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentLinkedQueue&lt;/code&gt; 类实现先进先出（first-in-first-out，FIFO）队列。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PriorityQueue&lt;/code&gt; 类实现优先级队列（也称为堆），它对于构建调度器非常有用，调度器必须按优先级或预期的执行时间执行任务。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;实现 Queue 的类是：&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LinkedList&lt;/code&gt; 已经进行了改进来实现 Queue。&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PriorityQueue&lt;/code&gt; 非线程安全的优先级队列（堆）实现，根据自然顺序或比较器返回元素。&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentLinkedQueue&lt;/code&gt; 快速、线程安全的、无阻塞 FIFO 队列。&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;弱一致的迭代器&quot;&gt;弱一致的迭代器&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;java.util 包中的集合类都返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fail-fast&lt;/code&gt; 迭代器，这意味着它们假设线程在集合内容中进行迭代时，集合不会更改它的内容。如果 fail-fast 迭代器检测到在迭代过程中进行了更改操作，那么它会抛出 &lt;strong&gt;ConcurrentModificationException&lt;/strong&gt;，这是不可控异常。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;java.util.concurrent 集合返回的迭代器称为弱一致的（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;weakly consistent&lt;/code&gt;）迭代器。对于这些类，如果元素自从迭代开始已经删除，且尚未由 next() 方法返回，那么它将不返回到调用者。如果元素自迭代开始已经添加，那么它可能返回调用者，也可能不返回。在一次迭代中，无论如何更改底层集合，元素不会被返回两次。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;线程池&quot;&gt;线程池&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;管理一大组小任务的标准机制是&lt;strong&gt;组合工作队列&lt;/strong&gt;和&lt;strong&gt;线程池&lt;/strong&gt;。工作队列就是要处理的任务的队列，前面描述的 Queue 类完全适合。线程池是线程的集合，每个线程都提取公用工作队列。当一个工作线程完成任务处理后，它会返回队列，查看是否有其他任务需要处理。如果有，它会转移到下一个任务，并开始处理。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;线程池为线程生命周期间接成本问题和资源崩溃问题提供了解决方案。&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;通过对多个任务重新使用线程，创建线程的间接成本将分布到多个任务中。&lt;/li&gt;
      &lt;li&gt;作为一种额外好处，因为请求到达时，线程已经存在，从而可以消除由创建线程引起的延迟, 因此，可以立即处理请求，使应用程序更易响应。&lt;/li&gt;
      &lt;li&gt;而且，通过正确调整线程池中的线程数，可以强制超出特定限制的任何请求等待，直到有线程可以处理它，它们等待时所消耗的资源要少于使用额外线程所消耗的资源，这样可以防止资源崩溃。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;executor-框架&quot;&gt;Executor 框架&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Executor&lt;/code&gt; 接口关注任务提交，确定执行策略。这使在部署时调整执行策略（队列限制、池大小、优先级排列等等）更加容易，更改的代码最少。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;大多数 Executor 实现类还实现 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecutorService&lt;/code&gt;接口，ExecutorService是Executor的子接口，它还管理执行服务的生命周期。这使它们更易于管理，并向生命可能比单独 Executor 的生命更长的应用程序提供服务。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;执行策略&lt;/strong&gt;定义何时在哪个线程中运行任务，执行任务可能消耗的资源级别（线程、内存等等），以及如果执行程序超载该怎么办。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;executorservice&quot;&gt;ExecutorService&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecutorService&lt;/code&gt;接口在提供了execute方法的同时，新加了更加通用的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;submit&lt;/code&gt;方法。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;通过submit方法返回的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Future&lt;/code&gt;对象可以读取&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Callable&lt;/code&gt;任务的执行结果，或管理Callable任务和Runnable任务的状态。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;ExecutorService也提供了批量运行Callable任务的方法，ExecutorService还提供了一些关闭执行器的方法。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;scheduledexecutorservice&quot;&gt;ScheduledExecutorService&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScheduledExecutorService&lt;/code&gt;扩展ExecutorService接口并添加了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schedule&lt;/code&gt;方法。调用schedule方法可以在指定的延时后执行一个Runnable或者Callable任务。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;ScheduledExecutorService接口还定义了按照指定时间间隔定期执行任务的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scheduleAtFixedRate&lt;/code&gt;方法和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scheduleWithFixedDelay&lt;/code&gt;方法。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;executors&quot;&gt;Executors&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Executors类&lt;/code&gt;包含用于构造许多不同类型的 Executor 实现的静态工厂方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newCachedThreadPool()&lt;/code&gt; 创建不限制大小的线程池，但是当以前创建的线程可以使用时将重新使用那些线程。如果没有现有线程可用，将创建新的线程并将其添加到池中。已有 60 秒钟未被使用的线程将其终止并从缓存中删除。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newFixedThreadPool(int n)&lt;/code&gt; 创建一个可重用固定线程数的线程池，以共享的无界队列方式来运行这些线程。如果在所有线程处于活动状态时提交附加任务，则在有可用线程之前，附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止，那么一个新线程将代替它执行后续的任务。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newSingleThreadExecutor()&lt;/code&gt; 创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务, 此线程池保证所有任务的执行顺序按照任务的提交顺序执行。如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newScheduledThreadPool(int n)&lt;/code&gt;  创建一个大小无限的线程池, 此线程池支持定时以及周期性执行任务的需求。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果上面的方法都不满足需要，可以尝试&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThreadPoolExecutor&lt;/code&gt;或者&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScheduledThreadPoolExecutor&lt;/code&gt;。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;定制-threadpoolexecutor&quot;&gt;定制 ThreadPoolExecutor&lt;/h2&gt;

&lt;p&gt;通过使用包含 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThreadFactory&lt;/code&gt; 变量的工厂方法或构造函数的版本，可以定义线程的创建。ThreadFactory 是工厂对象，其构造执行程序要使用的新线程。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DaemonThreadFactory&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThreadFactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDaemon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;有时，Executor 不能执行任务，因为它已经关闭或者因为 Executor 使用受限制队列存储等待任务，而该队列已满。在这种情况下，需要咨询执行程序的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RejectedExecutionHandler&lt;/code&gt; 来确定如何处理任务：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;抛出异常（默认情况），&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;放弃任务，&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在调用者的线程中执行任务，&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;或放弃队列中最早的任务以为新任务腾出空间。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setRejectedExecutionHandler&lt;/code&gt;可以设置拒绝的执行处理程序。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;需要特别考虑的问题&quot;&gt;需要特别考虑的问题&lt;/h2&gt;

&lt;p&gt;如果应用程序对特定执行程序进行了假设，那么应该在 Executor 定义和初始化的附近对这些进行说明，从而使善意的更改不会破坏应用程序的正确功能。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;一些任务可能同时等待其他任务完成。在这种情况下，当线程池没有足够的线程时，如果所有当前执行的任务都在等待另一项任务，而该任务因为线程池已满不能执行，那么线程池可能会死锁。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;一组线程必须作为共同操作组一起工作。在这种情况下，需要确保线程池能够容纳所有线程。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;调整线程池&quot;&gt;调整线程池&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;如果线程池太小，资源可能不能被充分利用，在一些任务还在工作队列中等待执行时，可能会有处理器处于闲置状态。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;另一方面，如果线程池太大，则将有许多有效线程，因为大量有效线程使用内存，或者因为每项任务要比使用少量线程有更多上下文切换，性能可能会受损。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Amdahl 法则提供很好的近似公式来确定线程池的大小：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WT&lt;/code&gt; 表示每项任务的平均等待时间，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ST&lt;/code&gt; 表示每项任务的平均服务时间（计算时间）。则 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WT/ST&lt;/code&gt; 是每项任务等待所用时间的百分比。对于 N 处理器系统，池中可以近似有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;N*(1+WT/ST)&lt;/code&gt; 个线程。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;future-接口&quot;&gt;Future 接口&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Future&lt;/code&gt; 接口允许表示已经完成的任务、正在执行过程中的任务或者尚未开始执行的任务。通过 Future 接口，可以尝试取消尚未完成的任务，查询任务已经完成还是取消了，以及提取（或等待）任务的结果值。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FutureTask&lt;/code&gt; 类实现了 Future，并包含一些构造函数，允许将 Runnable 或 Callable和 Future 接口封装。因为 FutureTask 也实现 Runnable，所以可以只将 FutureTask 提供给 Executor。一些提交方法（如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecutorService.submit()&lt;/code&gt;）除了提交任务之外，还将返回 Future 接口。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Future.get()&lt;/code&gt; 方法检索任务计算的结果, 如果任务尚未完成，那么 Future.get() 将被阻塞直到任务完成；如果任务已经完成，那么它将立即返回结果; 如果任务完成，但有异常，则抛出 ExecutionException。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用-future-构建缓存&quot;&gt;使用 Future 构建缓存&lt;/h2&gt;

&lt;p&gt;该示例利用 ConcurrentHashMap 中的原子 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;putIfAbsent()&lt;/code&gt; 方法，确保仅有一个线程试图计算给定关键字的值。如果其他线程随后请求同一关键字的值，它仅能等待（通过 Future.get() 的帮助）第一个线程完成。因此两个线程不会计算相同的值。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConcurrentMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FutureTask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Executor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newFixedThreadPool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Callable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExecutionException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;FutureTask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FutureTask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;FutureTask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ft&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;completionservice&quot;&gt;CompletionService&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CompletionService&lt;/code&gt; 将执行服务与类似 Queue 的接口组合，从任务执行中删除任务结果的处理。CompletionService 接口包含用来提交将要执行的任务的 submit() 方法和用来询问下一完成任务的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take()&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll()&lt;/code&gt; 方法。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;CompletionService 允许应用程序结构化，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Producer&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Consumer&lt;/code&gt; 模式，其中生产者创建任务并提交，消费者请求完成任务的结果并处理这些结果。CompletionService 接口由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecutorCompletionService&lt;/code&gt; 类实现，该类使用 Executor 处理任务并从 CompletionService 导出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;submit&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take&lt;/code&gt; 方法。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;下列代码使用 Executor 和 CompletionService 来启动许多任务，并使用第一个生成的非空结果，然后取消其余任务：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Executor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Callable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExecutionException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;CompletionService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ecs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExecutorCompletionService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ecs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ecs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;forkjoin&quot;&gt;Fork/Join&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fork/join&lt;/code&gt;框架是ExecutorService接口的一种具体实现，目的是为了更好地利用多处理器带来的好处。它是为那些能够被递归地拆解成子任务的工作类型量身设计的。其目的在于能够使用所有可用的运算能力来提升你的应用的性能。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;类似于ExecutorService接口的其他实现，fork/join框架会将任务分发给线程池中的工作线程。fork/join框架的独特之处在与它使用工作窃取(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-stealing&lt;/code&gt;)算法。完成自己的工作而处于空闲的工作线程能够从其他仍然处于忙碌(busy)状态的工作线程处窃取等待执行的任务。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;fork/join框架的核心是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForkJoinPool&lt;/code&gt;类，它是对&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbstractExecutorService&lt;/code&gt;类的扩展。ForkJoinPool实现了工作偷取算法，并可以执行ForkJoinTask任务。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在Java SE 8中，java.util.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Arrays&lt;/code&gt;类的一系列&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parallelSort()&lt;/code&gt;方法就使用了fork/join来实现。这些方法与sort()系列方法很类似，但是通过使用fork/join框架，借助了并发来完成相关工作, 在多处理器系统中，对大数组的并行排序会比串行排序更快。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;其他采用了fork/join框架的方法还有java.util.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;streams&lt;/code&gt;包中的一些方法，此包是Java SE 8发行版中Project Lambda的一部分。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;同步工具&quot;&gt;同步工具&lt;/h1&gt;

&lt;h2 id=&quot;semaphore&quot;&gt;Semaphore&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; 类实现标准 Dijkstra 计数信号。计数信号可以认为具有一定数量的许可权，该许可权可以获得或释放。如果有剩余的许可权，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acquire()&lt;/code&gt; 方法将成功，否则该方法将被阻塞，直到其他线程释放&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;release()&lt;/code&gt;许可权, 线程一次可以获得多个许可权。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;计数信号可以用于限制有权对资源进行并发访问的线程数。该方法对于实现资源池或限制 Web 爬虫（Web crawler）中的输出 socket 连接非常有用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;注意信号不跟踪哪个线程拥有多少许可权, 这由应用程序来决定，以确保何时线程释放许可权，该信号表示其他线程拥有许可权或者正在释放许可权，以及其他线程知道它的许可权已释放。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;互斥&quot;&gt;互斥&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;计数信号的一种特殊情况是&lt;strong&gt;互斥&lt;/strong&gt;，或者互斥信号。互斥就是具有单一许可权的计数信号，意味着在给定时间仅一个线程可以具有许可权, 互斥可以用于管理对共享资源的独占访问。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;虽然互斥许多地方与锁定一样，但互斥还有一个锁定通常没有的功能，就是互斥可以由不具有许可权的其他线程来释放, 这在死锁恢复时会非常有用。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cyclicbarrier&quot;&gt;CyclicBarrier&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CyclicBarrier&lt;/code&gt; 类可以帮助同步，它允许一组线程等待整个线程组到达公共屏障点。CyclicBarrier 是使用整型变量构造的，其确定组中的线程数。当一个线程到达屏障时（通过调用 CyclicBarrier.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await()&lt;/code&gt;），它会被阻塞，直到所有线程都到达屏障，然后在该点允许所有线程继续执行。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;CyclicBarrier可以重新使用, 一旦所有线程都已经在屏障处集合并释放，则可以将该屏障重新初始化到它的初始状态。 还可以指定在屏障处等待时的超时；如果在该时间内其余线程还没有到达屏障，则认为屏障被打破，所有正在等待的线程会收到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BrokenBarrierException&lt;/code&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;下列代码将创建 CyclicBarrier 并启动一组线程，每个线程在到达屏障点前会打印出自己的名字，等待其他线程到齐后，将执行CyclicBarrier绑定的Runnable，该Runnable在每个屏障点只执行一次。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ready&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;CyclicBarrier&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyclicBarrier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CyclicBarrier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;ExecutorService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newCachedThreadPool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;cyclicBarrier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BrokenBarrierException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;countdownlatch&quot;&gt;CountdownLatch&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountdownLatch&lt;/code&gt; 类与 CyclicBarrier 相似，因为它的角色是对已经在它们中间分摊了问题的一组线程进行协调。它也是使用整型变量构造的，指明计数的初始值，但是与 CyclicBarrier 不同的是，CountdownLatch 不能重新使用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;其中，CyclicBarrier 是到达屏障的所有线程的大门，只有当所有线程都已经到达屏障或屏障被打破时，才允许这些线程通过。CountdownLatch 将到达和等待功能分离，任何线程都可以通过调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;countDown()&lt;/code&gt; 减少当前计数，这种方式不会阻塞线程，而只是减少计数。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await()&lt;/code&gt; 方法的行为与 CyclicBarrier.await() 稍微有所不同，调用 await() 任何线程都会被阻塞，直到闩锁计数减少为零，在该点等待的所有线程才被释放，对 await() 的后续调用将立即返回。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当问题已经分解为许多部分，每个线程都被分配一部分计算时，CountdownLatch 非常有用。在工作线程结束时，它们将减少计数，协调线程可以在闩锁处等待当前这一批计算结束，然后继续移至下一批计算。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrency&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ExecutorService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newCachedThreadPool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountDownLatch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountDownLatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concurrency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountDownLatch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountDownLatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountDownLatch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountDownLatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concurrency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concurrency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;countDown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;----done&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;countDown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;all threads are ready&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;countDown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;all threads finished&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;exchanger&quot;&gt;Exchanger&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exchanger&lt;/code&gt; 类方便了两个共同操作线程之间的双向交换，就像具有计数为 2 的 CyclicBarrier，并且两个线程在都到达屏障时可以”交换”一些状态。（Exchanger 模式有时也称为聚集。）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Exchanger 通常用于一个线程填充缓冲（通过读取 socket），而另一个线程清空缓冲（通过处理从 socket 收到的命令）的情况。当两个线程在屏障处集合时，它们交换缓冲。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;下列代码说明了这项技术：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Exchanger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exchanger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exchanger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ExecutorService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newCachedThreadPool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;produce&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-offer----&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;exchanger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exchange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exchanger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exchange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-get----&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;lock工具&quot;&gt;Lock工具&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lock&lt;/code&gt; 接口将内置监视器锁定的行为普遍化，允许多个锁定实现，同时提供一些内置锁定缺少的功能，如&lt;strong&gt;计时&lt;/strong&gt;的等待、&lt;strong&gt;可中断&lt;/strong&gt;的等待、锁定&lt;strong&gt;轮询&lt;/strong&gt;、每个锁定有多个&lt;strong&gt;条件&lt;/strong&gt;等待集合以及&lt;strong&gt;无阻塞&lt;/strong&gt;结构的锁定。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;java.util.concurrent.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locks&lt;/code&gt;包提供了复杂的锁, 锁对象作用非常类似同步代码使用的隐式锁, 每次只有一个线程可以获得锁对象。通过关联&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Condition&lt;/code&gt;对象，锁对象也支持&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait/notify&lt;/code&gt;机制。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;锁对象之于隐式锁最大的优势在于，它们&lt;strong&gt;有能力收回获取锁的尝试&lt;/strong&gt;（如果获取锁失败，将不会继续请求，以免发生死锁）。如果当前锁对象不可用，或者锁请求超时（如果超时时间已指定），&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tryLock&lt;/code&gt;方法会收回获取锁的请求。如果在锁获取前，另一个线程发送了一个中断，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lockInterruptibly&lt;/code&gt;方法也会收回获取锁的请求。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;reentrantlock&quot;&gt;ReentrantLock&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReentrantLock&lt;/code&gt; 是具有与隐式监视器锁定（使用 synchronized 方法和语句访问）相同的基本行为和语义的 Lock 的实现，但它具有扩展的能力。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在竞争条件下，ReentrantLock 的实现要比现在的 synchronized 实现更具有可伸缩性。这意味着当许多线程都竞争相同锁定时，使用 ReentrantLock 的吞吐量通常要比 synchronized 好。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;虽然 ReentrantLock 类有许多优点，但是与同步相比，它有一个主要&lt;strong&gt;缺点&lt;/strong&gt; – 它可能忘记释放锁定。因为锁定失误（忘记释放锁定）的风险，所以对于基本锁定，强烈建议继续使用 synchronized，除非真的需要 ReentrantLock 额外的灵活性和可伸缩性。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;建议当获得和释放 ReentrantLock 时使用下列结构：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReentrantLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// perform operations protected by lock&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;// restore invariants&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;condition&quot;&gt;Condition&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;就像 Lock 接口是同步的具体化，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Condition&lt;/code&gt; 接口是 Object 中 wait() 和 notify() 方法的具体化。Lock 中的一个方法是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newCondition()&lt;/code&gt;，它要求该锁定返回新的 Condition 对象限制。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await()&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;signal()&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;signalAll()&lt;/code&gt; 方法类似于 wait()、notify() 和 notifyAll()，但增加了灵活性，每个 Lock 都可以创建多个条件变量。这简化了一些并发算法的实现。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;readwritelock&quot;&gt;ReadWriteLock&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;ReentrantLock 实现的锁定规则非常简单 – 每当一个线程具有锁定时，其他线程必须等待，直到该锁定可用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;有时，当对数据结构的读取通常多于修改时，可以使用更复杂的称为读写锁定的锁定结构，它允许有多个并发读者，同时还允许一个写入者独占锁定。该方法在一般情况下（只读）提供了更大的并发性，同时在必要时仍提供独占访问的安全性。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadWriteLock&lt;/code&gt; 接口和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReentrantReadWriteLock&lt;/code&gt; 类提供这种功能 – &lt;strong&gt;多读者&lt;/strong&gt;、&lt;strong&gt;单写入&lt;/strong&gt;者锁定规则，可以用这种功能来保护共享的易变资源。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;原子变量&quot;&gt;原子变量&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;java.util.concurrent.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;atomic&lt;/code&gt;包定义了对单一变量进行原子操作的类。所有的类都提供了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set&lt;/code&gt;方法，可以使用它们像读写&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;volatile&lt;/code&gt;变量一样读写原子类。就是说，同一变量上的一个set操作对于任意后续的get操作存在happens-before关系。原子的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compareAndSet&lt;/code&gt;方法也有内存一致性特点，就像应用到整型原子变量中的简单原子算法。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;即使大多数用户将很少直接使用它们，原子变量类（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AtomicInteger&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AtomicLong&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AtomicReference&lt;/code&gt; 等等）也有充分理由是最显著的新并发类。这些类公开对 JVM 的低级别改进，允许进行具有高度可伸缩性的&lt;strong&gt;原子读-修改-写操作&lt;/strong&gt;。大多数现代 CPU 都有原子读-修改-写的原语，比如比较并交换（CAS）或加载链接/条件存储（LL/SC）。原子变量类使用硬件提供的最快的并发结构来实现。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;几乎 java.util.concurrent 中的所有类都是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReentrantLock&lt;/code&gt; 之上构建的，ReentrantLock 则是在&lt;strong&gt;原子变量类&lt;/strong&gt;的基础上构建的。所以，虽然仅少数并发专家使用原子变量类，但 java.util.concurrent 类的很多可伸缩性改进都是由它们提供的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;原子变量主要用于为原子地更新 “热” 字段提供有效的、细粒度的方式， “热” 字段是指由多个线程频繁访问和更新的字段。另外，原子变量还是计数器或生成序号的自然机制。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;并发随机数&quot;&gt;并发随机数&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;在JDK7中，java.util.concurrent包含了一个相当便利的类，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThreadLocalRandom&lt;/code&gt;，当应用程序期望在多个线程或ForkJoinTasks中使用随机数时。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于并发访问，使用TheadLocalRandom代替Math.random()可以减少竞争，从而获得更好的性能。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;只需调用ThreadLocalRandom.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;current()&lt;/code&gt;， 然后调用它的其中一个方法去获取一个随机数即可。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;性能与可伸缩性&quot;&gt;性能与可伸缩性&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;性能&lt;/strong&gt;是 “可以快速执行此任务的程度” 的评测。&lt;strong&gt;可伸缩性&lt;/strong&gt;描述应用程序的&lt;strong&gt;吞吐量如何表现为它的工作量和可用计算资源增加&lt;/strong&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;可伸缩的程序可以按比例使用更多的处理器、内存或 I/O 带宽来处理更多个工作量。当我们在并发环境中谈论可伸缩性时，我们是在问当许多线程同时访问给定类时，这个类的执行情况。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;java.util.concurrent 中的低级别类 ReentrantLock 和原子变量类的可伸缩性要比内置监视器（同步）锁定高得多。因此，使用 ReentrantLock 或原子变量类来协调共享访问的类也可能更具有可伸缩性。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 18 May 2014 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/java/java-concurrent/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/java/java-concurrent/</guid>
        
        <category>Java</category>
        
        <category>Concurrent</category>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>乱码现象分析</title>
        <description>&lt;p&gt;我们知道，web浏览器会将form中的内容打包成HTTP请求体，然后发送到服务端，服务端对请求体解析后可以得到传递的数据。这当中包含两个过程：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;encode&lt;/code&gt;与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decode&lt;/code&gt;。&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#http&quot; id=&quot;markdown-toc-http&quot;&gt;HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#urlencode&quot; id=&quot;markdown-toc-urlencode&quot;&gt;UrlEncode&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#urldecode&quot; id=&quot;markdown-toc-urldecode&quot;&gt;UrlDecode&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#方案&quot; id=&quot;markdown-toc-方案&quot;&gt;方案&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;http&quot;&gt;HTTP&lt;/h1&gt;

&lt;p&gt;我们使用ServerSocket搭建一个小服务器来看清http请求的全貌, 该服务器只有一个功能, 就是打印请求体。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpPrint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServerSocket&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpPrint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;serverSocket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServerSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Socket&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;InputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpPrint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用html页面来发送get与post请求&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8080/hsp?param=你好全世界&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Test&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8080/hsp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;param&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;你好全世界&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动服务器后，查看打印内容，在我的机器上，请求内容如下:&lt;/p&gt;

&lt;p&gt;get&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/get.png&quot; alt=&quot;get&quot; /&gt;&lt;/p&gt;

&lt;p&gt;post&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/post.png&quot; alt=&quot;post&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从post中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content-Type:application/x-www-form-urlencoded&lt;/code&gt;可以看到，虽然数据为中文，但是在传递的时候，经过了一次urlEncode，这样一来，在数据交换层面就可以屏蔽编码的不一致性。&lt;/p&gt;

&lt;h1 id=&quot;urlencode&quot;&gt;UrlEncode&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlEncode&lt;/code&gt;的任务是将form中的数据进行编码, 编码过程非常简单, 任何字符只要不是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ASCII&lt;/code&gt;码, 它们都将被转换成字节形式, 每个字节都写成这种形式：一个 “%” 后面跟着两位16进制的数值。
urlEncode只能识别ASCII码，可以想象的是，那些urlEncode不能识别的字符，也就是十六进制数，一定是依赖于特定的字符集产生的, 字符集包括unicode,iso等。&lt;/p&gt;

&lt;p&gt;那么浏览器用的是什么字符集呢? 答案是：默认与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contentType&lt;/code&gt;相同, form可以通过属性&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accept-charset&lt;/code&gt;指定。&lt;/p&gt;

&lt;p&gt;例如我们通常可以在jsp中看到这样的设置:&lt;/p&gt;

&lt;div class=&quot;language-jsp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;%@page &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contentType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/html;charset=UTF-8&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者在html中这样设置:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/html; charset=UTF-8&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这表示浏览器得到响应流之后，用contentType指定的字符集，将流中的字节转换为字符，同样地，也会用这个字符集将页面中字符转换为字节。&lt;/p&gt;

&lt;p&gt;关于浏览器设定字符集的问题，我们不过多讨论，现在只需要知道有这么个过程就行了, 需要注意的是，无论浏览器使用什么字符集，服务端都是无法获知的。
这里需要换位考虑一下，浏览器是一个客户端，应该让客户端 “迁就” 服务端, 所以浏览器请求一个服务的时候，应该让浏览器考虑服务端支持什么字符集, 得到了响应后, 用服务端告诉浏览器的字符集进行解析。&lt;/p&gt;

&lt;h1 id=&quot;urldecode&quot;&gt;UrlDecode&lt;/h1&gt;

&lt;p&gt;现在我们将目光转向Servlet, 并使用上面的html来请求服务，请确保请求的字符集为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode&lt;/code&gt;, 应用服务器使用tomcat6。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletPrint&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doPost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletResponse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;param&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doGet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletResponse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doPost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;get与post结果如下，果然如预料中乱码了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/param.png&quot; alt=&quot;param&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在我们从Servlet中看看请求体, 修改上面的Servlet代码如下：&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletPrint&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doPost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletResponse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//System.out.println(req.getParameter(&quot;param&quot;));&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;InputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&amp;gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;URLDecoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doGet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpServletResponse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doPost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;get与post结果如下,Servlet将http头部解析完成后，将请求体留了下来供应用程序使用, 这是考虑到http请求可能有多种 &lt;a href=&quot;http://www.w3school.com.cn/tags/att_form_enctype.asp&quot;&gt;enctype&lt;/a&gt; , 请求体的结构可能不同,
例如，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipart/form-data&lt;/code&gt;就不是这样的key=value结构，关于multipart/form-data，我在这篇 &lt;a href=&quot;https://bit-ranger.github.io/blog/web/2014-03-15/file-upload&quot;&gt;fileupload&lt;/a&gt; 中曾有过简要分析。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/body-utf.png&quot; alt=&quot;body-utf&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图中可以看到, 设置了正确的字符集后, 服务端将能够正确地解析, get部分什么都没有，这是因为get没有请求体。&lt;/p&gt;

&lt;p&gt;让我们换种字符集试试, 比如, 将servet中的”utf-8”换成”iso-8859-1”。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/body-iso.png&quot; alt=&quot;body-iso&quot; /&gt;&lt;/p&gt;

&lt;p&gt;嘿，果然如我所料，而且这个字符串好像很眼熟，和req.getParameter(“param”)结果是一样的，没错，事实上，tomcat默认的字符集就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iso-8859-1&lt;/code&gt;, 我们从中可以得到一个推论，tomcat使用默认的字符集，对http请求进行过一次decode。&lt;/p&gt;

&lt;h1 id=&quot;方案&quot;&gt;方案&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlDecode&lt;/code&gt;的任务是将请求中的百分号码转换成字符，显而易见的是，使用与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlEncode&lt;/code&gt;时相同的字符集才能成功转换。通常的做法是，让服务端支持涵盖多国语言的”utf-8”，然后让客户端也用”utf-8”请求服务。&lt;/p&gt;

&lt;p&gt;指定服务端字符集的方式有两种，一是修改应用服务器的默认编码，二是添加一个过滤器进行编码转换, 方法一最方便, 但是影响了程序的可移植性, 方法二可移植, 它只需要做一件事:&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requet.setCharacterEncoding(&quot;UTF-8&quot;);&lt;/code&gt;,
实际上，该过滤器并没有进行任何编码转换的工作，它仅仅只是一个配置，该配置项将被后续程序使用，这些后续程序包括web服务器内置的解析程序，以及第三方解析工具等。&lt;/p&gt;

&lt;p&gt;需要注意的是，requet.setCharacterEncoding(“UTF-8”);，只对请求体有效，也就是说，请求头不归它管，而是由web服务器采用自己配置的字符编码进行解析，此时如果url中包含中文（如get请求的参数），那么将不可避免地出现字符丢失。
解决办法是在客户端对url进行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;encodeURI&lt;/code&gt;&lt;strong&gt;两次&lt;/strong&gt;, 然后再在服务端&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URLDecoder.decode(param,&quot;utf-8&quot;);&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;为什么要 &lt;a href=&quot;http://www.w3school.com.cn/jsref/jsref_encodeuri.asp&quot;&gt;encodeURI&lt;/a&gt; 两次？talk is cheap， let’s code！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/encodeURI.png&quot; alt=&quot;encodeURI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意观察这张图片，从中发现了什么? 没错，第一次encodeURI生成了HTTP一节的示例中一样的结果。
我们在浏览器窗口中输入 “http://localhost:8080/hsp?param=%E4%BD%A0%E5%A5%BD%E5%85%A8%E4%B8%96%E7%95%8C”, 会发现它变成了 “http://localhost:8080/hsp?param=你好全世界”,
在url里，浏览器认为%是个转义字符，浏览器会把%与%之间的编码，两位两位取出后进行decode, 也就是变回 “你好全世界”, 然后再用这个url发送请求, 最终实际发送的内容实际上还是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%E4%BD%A0%E5%A5%BD%E5%85%A8%E4%B8%96%E7%95%8C&lt;/code&gt;。
换言之，以明文传递的这种url会被浏览器否决一次，再换言之，在js中进行一次encodeURI等于什么都没做。&lt;/p&gt;

&lt;p&gt;再注意观察第2和第3个输出，有什么规律? 是的，从第二次开始encodeURI只是将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt;变成了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%25&lt;/code&gt;，
根据我们刚才总结出的规律可知，在encodeURI两次的情况下，最后发送到浏览器中的数据为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%25E4%25BD%25A0%25E5%25A5%25BD%25E5%2585%25A8%25E4%25B8%2596%25E7%2595%258C&lt;/code&gt;,
理所当然的，web服务器将使用默认的字符集对其decode, 然而, 无论选择哪种字符集, 将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%25&lt;/code&gt;转换成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt;总是不会出错的, decode之后，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%E4%BD%A0%E5%A5%BD%E5%85%A8%E4%B8%96%E7%95%8C&lt;/code&gt; 将完整地送到Servlet手上。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;URLDecoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;param&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://localhost:8080/hsp?param=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;encodeURI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;encodeURI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;你好全世界&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/servlet-encode/world.png&quot; alt=&quot;world&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 05 Apr 2014 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/web/encode-decode/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/web/encode-decode/</guid>
        
        <category>servlet</category>
        
        <category>encode</category>
        
        <category>Java</category>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>解析HTTP, 实现文件上传</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#http&quot; id=&quot;markdown-toc-http&quot;&gt;HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#分析&quot; id=&quot;markdown-toc-分析&quot;&gt;分析&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#设计&quot; id=&quot;markdown-toc-设计&quot;&gt;设计&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#关键代码&quot; id=&quot;markdown-toc-关键代码&quot;&gt;关键代码&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#源码获取&quot; id=&quot;markdown-toc-源码获取&quot;&gt;源码获取&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文的目的是简要说明如何编写一个文件上传组件，使他的功能类似 &lt;a href=&quot;https://github.com/apache/commons-fileupload&quot;&gt;commons-fileupload&lt;/a&gt;, 并在结尾处提供了完整代码的获取方式。&lt;/p&gt;

&lt;h1 id=&quot;http&quot;&gt;HTTP&lt;/h1&gt;
&lt;p&gt;本文讨论的是基于 HTTP 协议的文件上传，下面先来看看 HTTP 请求的真面目。&lt;/p&gt;

&lt;p&gt;首先，用 JavaSe 类库中的 Socket 搭建一个超简单的服务器，这个服务器只有一个功能，就是完整地打印整个 HTTP 请求体。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServerSocket&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;serverSocket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServerSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Socket&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serverSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;InputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;OutputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将服务器运行起来之后，在浏览器中输入地址：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;在我的机器上，显示如下内容，可以看到，这个一个get请求&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/http-get.png&quot; alt=&quot;http-get&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面利用一个 html 的 form表单提交 post 请求&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8080&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;enctype=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1970-01-01&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在我的机器上，显示如下内容&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/http-post.png&quot; alt=&quot;http-post&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意图中被红色框起来的部分，第一个红框指示了本次请求中，用来分隔不同元素的分隔线。&lt;/p&gt;

&lt;p&gt;每个元素将以此分隔线作为第一行，后面紧跟对元素的描述，描述与内容用空行分隔。&lt;/p&gt;

&lt;p&gt;分隔线的后面加两个小短横代表整个请求体结束，即&lt;strong&gt;EOF&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;我们需要做的工作，就是利用分隔线，从请求体中分离出每个元素，分析HTTP请求头的工作可以交给Servlet。&lt;/p&gt;

&lt;h1 id=&quot;分析&quot;&gt;分析&lt;/h1&gt;

&lt;p&gt;那么，如何分离呢？&lt;/p&gt;

&lt;p&gt;java中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputStream&lt;/code&gt; 只能读取一次，所以我们想要方便地分析一个流，最直接的办法就是将其缓存下来。&lt;/p&gt;

&lt;p&gt;RandomAccessFile 或许能够满足需求，RandomAccessFile 可以提供一个指针用于在文件中的随意移动，然而需要读写本地文件的方案不会是最优方案。&lt;/p&gt;

&lt;p&gt;先将整个流读一遍将内容缓存到内存中？ 这种方案在多个客户端同时提交大文件时一定是不可靠的。&lt;/p&gt;

&lt;p&gt;最理想的方案可能是，我只需要读一遍 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputStream&lt;/code&gt; , 读完后将得到一个有序列表，列表中存放每个元素对象。&lt;/p&gt;

&lt;p&gt;很明显，JavaSe的流没有提供这个功能&lt;/p&gt;

&lt;p&gt;我们知道从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputStreeam&lt;/code&gt; 中获取内容需要使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt; 方法，返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt; 表示读到了流的末尾，如果我们增强一下&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;的功能，让其在读到每个元素末尾的时候返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt;，这样不就可以分离出每个元素了吗，至于判断是否到了整个流的末尾，自有办法。&lt;/p&gt;

&lt;h1 id=&quot;设计&quot;&gt;设计&lt;/h1&gt;

&lt;p&gt;如何增强&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;方法呢？&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;方法要在读到元素末尾时返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt; , 一定需要先对已读取的内容进行分析，判断是否元素末尾。&lt;/p&gt;

&lt;p&gt;我的做法是，内部维护一个buffer，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;方法在读取时先将字节写入到这个buffer中，然后分析其中是否存在分隔线，然后将buffer中可用的元素复制到客户端提供的buffer。&lt;/p&gt;

&lt;p&gt;这个内部维护的buffer并不总是满的，其中的字节来自read方法的原始功能，所以我们需要一个变量来记录buffer中有效字节的末尾位置 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;我们还需要一个变量 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pos&lt;/code&gt; 来标记buffer中是否存在分隔线，pos的值即为分隔线的开头在buffer中的位置，如果buffer中不存在分隔线pos的值将为-1。&lt;/p&gt;

&lt;p&gt;但是问题没这个简单，分隔线在buffer中存在状态有两种情况：&lt;/p&gt;

&lt;p&gt;情况A，分隔线完好地存在于buffer中，图中的bundary即为分隔线&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/boundary-A.png&quot; alt=&quot;boundary-A&quot; /&gt;&lt;/p&gt;

&lt;p&gt;情况B，分隔线的一部分存在于buffer中&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/boundary-B.png&quot; alt=&quot;boundary-B&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在B情况下，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;boundary&lt;/code&gt;有多少字节存在于buffer中是不确定的，而且依靠这些不完整的字节根本无法判断他是否属于boundary开头。&lt;/p&gt;

&lt;p&gt;例如，buffer中没有发现boundary，但是buffer末尾的3个字节与boundary开头相同，这种情况可能只是巧合，boundary并没有被截断。&lt;/p&gt;

&lt;p&gt;对于这个问题，有一个解决办法，我们不必检查到buffer末尾，而是在buffer末尾留一个关健区&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pad&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这个关健区中很有可能存在被截断boundary，每次检查到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pad&lt;/code&gt;开头时立即收手，此位置之前的数据可以确保没有boundary，在下次填充buffer时，将这个关健区中的数据复制到buffer开头再处理。很显然，关健区&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pad&lt;/code&gt;长度应该等于boundary，如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://bit-ranger.github.io/blog/static/img/boundary-C.png&quot; alt=&quot;pad&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;关键代码&quot;&gt;关键代码&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;在buffer中检查boundary&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;findSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//若buffer中head至tail之间的字节数小于boundaryLength,那么maxpos将小于head,循环将不会运行,返回值为-1&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxpos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boundaryLength&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxpos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boundaryLength&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findByte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;boundary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxpos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boundaryLength&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boundary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boundaryLength&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;填充buffer&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makeAvailable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//该方法在available返回0时才会被调用,若pos!=-1那pos==head,表示boundary处于head位,可用字节数为0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 将pad位之后的数据移动到buffer开头&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;arraycopy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 将buffer填满&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//循环读取数据,直至将buffer填满,在此过程中,每次读取都将检索buffer中是否存在boundary,无论存在与否,都将即时返回可用数据量&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(;;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytesRead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bufSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytesRead&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//理论上因为会对buffer不断进行检索,读到boundary时就会return 0,read方法将返回 -1,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//所以不会读到input末尾,如果运行到了这里,表示发生了错误.&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Stream ended unexpectedly&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;notifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;notifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;noteBytesRead&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytesRead&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytesRead&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;findSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//若buffer中的数据量小于keepRegion(boundaryLength),av将必定等于0,循环将继续,直至数据量大于或等于keepRegion(boundaryLength).&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//此时将检索buffer中是否包含boundary,若包含,将返回boundary所在位置pos之前的数据量,若不包含,将返回pad位之前的数据量&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;av&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;available&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;av&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;av&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;强化后的read方法&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;closed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;the stream is closed&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;available&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makeAvailable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;arraycopy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;源码获取&quot;&gt;源码获取&lt;/h1&gt;

&lt;p&gt;我已经按照这套想法完整地实现了文件上传组件&lt;/p&gt;

&lt;p&gt;有兴趣的朋友可以从我的Gighub获取源码 &lt;a href=&quot;https://github.com/bit-ranger/http-multipart/releases&quot;&gt;点我获取&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;使用方法：&lt;a href=&quot;https://github.com/bit-ranger/http-multipart&quot;&gt;点我查看&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 15 Mar 2014 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/web/file-upload/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/web/file-upload/</guid>
        
        <category>file-upload</category>
        
        <category>Java</category>
        
        <category>Http</category>
        
        <category>multi-part</category>
        
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>Effective Java</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#一-创建和销毁对象&quot; id=&quot;markdown-toc-一-创建和销毁对象&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;一&lt;/code&gt; 创建和销毁对象&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#1考虑用静态工厂方法代替构造器&quot; id=&quot;markdown-toc-1考虑用静态工厂方法代替构造器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.考虑用静态工厂方法代替构造器&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#2遇到多个构造器参数时要考虑用构建器&quot; id=&quot;markdown-toc-2遇到多个构造器参数时要考虑用构建器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.遇到多个构造器参数时要考虑用构建器&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#3用私有构造器或者枚举类型强化singleton属性&quot; id=&quot;markdown-toc-3用私有构造器或者枚举类型强化singleton属性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.用私有构造器或者枚举类型强化Singleton属性&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#4通过私有构造器强化不可实例化的能力&quot; id=&quot;markdown-toc-4通过私有构造器强化不可实例化的能力&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.通过私有构造器强化不可实例化的能力&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#5避免创建不必要的对象&quot; id=&quot;markdown-toc-5避免创建不必要的对象&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.避免创建不必要的对象&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#6消除过期的对象引用&quot; id=&quot;markdown-toc-6消除过期的对象引用&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.消除过期的对象引用&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#7避免使用终结方法&quot; id=&quot;markdown-toc-7避免使用终结方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.避免使用终结方法&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#二-通用方法&quot; id=&quot;markdown-toc-二-通用方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;二&lt;/code&gt; 通用方法&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#8覆盖equals时请遵守通用约定&quot; id=&quot;markdown-toc-8覆盖equals时请遵守通用约定&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.覆盖equals时请遵守通用约定&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#9覆盖equals时总要覆盖hashcode&quot; id=&quot;markdown-toc-9覆盖equals时总要覆盖hashcode&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.覆盖equals时总要覆盖hashCode&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#10始终要覆盖tostring&quot; id=&quot;markdown-toc-10始终要覆盖tostring&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;.始终要覆盖toString&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#11谨慎地覆盖clone&quot; id=&quot;markdown-toc-11谨慎地覆盖clone&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11&lt;/code&gt;.谨慎地覆盖clone&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#12考虑实现comparable接口&quot; id=&quot;markdown-toc-12考虑实现comparable接口&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12&lt;/code&gt;.考虑实现Comparable接口&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#三-类和接口&quot; id=&quot;markdown-toc-三-类和接口&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;三&lt;/code&gt; 类和接口&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#13使类和成员的可访问性最小化&quot; id=&quot;markdown-toc-13使类和成员的可访问性最小化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;13&lt;/code&gt;.使类和成员的可访问性最小化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#14在公有类中使用访问方法而非公有域&quot; id=&quot;markdown-toc-14在公有类中使用访问方法而非公有域&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;14&lt;/code&gt;.在公有类中使用访问方法而非公有域&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#15使可变性最小化&quot; id=&quot;markdown-toc-15使可变性最小化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15&lt;/code&gt;.使可变性最小化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#16复合优先于继承&quot; id=&quot;markdown-toc-16复合优先于继承&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;16&lt;/code&gt;.复合优先于继承&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#17要么为继承而设计并提供文档说明要么就禁止继承&quot; id=&quot;markdown-toc-17要么为继承而设计并提供文档说明要么就禁止继承&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;17&lt;/code&gt;.要么为继承而设计，并提供文档说明，要么就禁止继承&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#18接口优于抽象类&quot; id=&quot;markdown-toc-18接口优于抽象类&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18&lt;/code&gt;.接口优于抽象类&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#19接口只用于定义类型&quot; id=&quot;markdown-toc-19接口只用于定义类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;19&lt;/code&gt;.接口只用于定义类型&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#20类层次优于标签类&quot; id=&quot;markdown-toc-20类层次优于标签类&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;20&lt;/code&gt;.类层次优于标签类&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#21用函数对象表示策略&quot; id=&quot;markdown-toc-21用函数对象表示策略&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;21&lt;/code&gt;.用函数对象表示策略&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#22优先考虑静态成员类&quot; id=&quot;markdown-toc-22优先考虑静态成员类&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;22&lt;/code&gt;.优先考虑静态成员类&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#四-泛型&quot; id=&quot;markdown-toc-四-泛型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;四&lt;/code&gt; 泛型&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#23不要在新代码中使用原生态类型&quot; id=&quot;markdown-toc-23不要在新代码中使用原生态类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;23&lt;/code&gt;.不要在新代码中使用原生态类型&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#24消除非受检警告&quot; id=&quot;markdown-toc-24消除非受检警告&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24&lt;/code&gt;.消除非受检警告&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#25列表优先于数组&quot; id=&quot;markdown-toc-25列表优先于数组&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;25&lt;/code&gt;.列表优先于数组&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#26优先考虑泛型&quot; id=&quot;markdown-toc-26优先考虑泛型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;26&lt;/code&gt;.优先考虑泛型&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#27优先考虑泛型方法&quot; id=&quot;markdown-toc-27优先考虑泛型方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;27&lt;/code&gt;.优先考虑泛型方法&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#28利用有限通配符来提升api的灵活性&quot; id=&quot;markdown-toc-28利用有限通配符来提升api的灵活性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;28&lt;/code&gt;.利用有限通配符来提升API的灵活性&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#29优先考虑类型安全的异构容器&quot; id=&quot;markdown-toc-29优先考虑类型安全的异构容器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;29&lt;/code&gt;.优先考虑类型安全的异构容器&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#五-枚举和注解&quot; id=&quot;markdown-toc-五-枚举和注解&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;五&lt;/code&gt; 枚举和注解&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#30用enum代替int常量&quot; id=&quot;markdown-toc-30用enum代替int常量&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;30&lt;/code&gt;.用enum代替int常量&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#31用实例域代替序数&quot; id=&quot;markdown-toc-31用实例域代替序数&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;31&lt;/code&gt;.用实例域代替序数&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#32用enumset代替位域&quot; id=&quot;markdown-toc-32用enumset代替位域&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;32&lt;/code&gt;.用EnumSet代替位域&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#33用enummap代替序数索引&quot; id=&quot;markdown-toc-33用enummap代替序数索引&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;33&lt;/code&gt;.用EnumMap代替序数索引&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#34用接口模拟可伸缩的枚举&quot; id=&quot;markdown-toc-34用接口模拟可伸缩的枚举&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;34&lt;/code&gt;.用接口模拟可伸缩的枚举&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#35注解优先于命名模式&quot; id=&quot;markdown-toc-35注解优先于命名模式&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;35&lt;/code&gt;.注解优先于命名模式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#36坚持使用override注解&quot; id=&quot;markdown-toc-36坚持使用override注解&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;36&lt;/code&gt;.坚持使用Override注解&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#37用标记接口定义类型&quot; id=&quot;markdown-toc-37用标记接口定义类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;37&lt;/code&gt;.用标记接口定义类型&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#六-方法&quot; id=&quot;markdown-toc-六-方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;六&lt;/code&gt; 方法&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#38检查参数的有效性&quot; id=&quot;markdown-toc-38检查参数的有效性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;38&lt;/code&gt;.检查参数的有效性&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#39必要时进行保护性拷贝&quot; id=&quot;markdown-toc-39必要时进行保护性拷贝&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;39&lt;/code&gt;.必要时进行保护性拷贝&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#40谨慎设计方法签名&quot; id=&quot;markdown-toc-40谨慎设计方法签名&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;40&lt;/code&gt;.谨慎设计方法签名&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#41慎用重载&quot; id=&quot;markdown-toc-41慎用重载&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;41&lt;/code&gt;.慎用重载&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#42慎用可变参数&quot; id=&quot;markdown-toc-42慎用可变参数&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;42&lt;/code&gt;.慎用可变参数&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#43返回零长度的数组或者集合而不是null&quot; id=&quot;markdown-toc-43返回零长度的数组或者集合而不是null&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;43&lt;/code&gt;.返回零长度的数组或者集合，而不是null&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#44为所有导出的api元素编写文档注释&quot; id=&quot;markdown-toc-44为所有导出的api元素编写文档注释&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;44&lt;/code&gt;.为所有导出的API元素编写文档注释&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#七-通用程序设计&quot; id=&quot;markdown-toc-七-通用程序设计&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;七&lt;/code&gt; 通用程序设计&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#45将局部变量的作用域最小化&quot; id=&quot;markdown-toc-45将局部变量的作用域最小化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;45&lt;/code&gt;.将局部变量的作用域最小化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#46for-each-循环优先于传统for循环&quot; id=&quot;markdown-toc-46for-each-循环优先于传统for循环&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;46&lt;/code&gt;.for-each 循环优先于传统for循环&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#47了解和使用类库&quot; id=&quot;markdown-toc-47了解和使用类库&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;47&lt;/code&gt;.了解和使用类库&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#48如果需要精确的答案请避免使用float和double&quot; id=&quot;markdown-toc-48如果需要精确的答案请避免使用float和double&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;48&lt;/code&gt;.如果需要精确的答案，请避免使用float和double&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#49基本类型优先于装箱基本类型&quot; id=&quot;markdown-toc-49基本类型优先于装箱基本类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;49&lt;/code&gt;.基本类型优先于装箱基本类型&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#50如果其他类型更适合则尽量避免使用字符串&quot; id=&quot;markdown-toc-50如果其他类型更适合则尽量避免使用字符串&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50&lt;/code&gt;.如果其他类型更适合，则尽量避免使用字符串&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#51当心字符串连接的性能&quot; id=&quot;markdown-toc-51当心字符串连接的性能&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;51&lt;/code&gt;.当心字符串连接的性能&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#52通过接口引用对象&quot; id=&quot;markdown-toc-52通过接口引用对象&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;52&lt;/code&gt;.通过接口引用对象&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#53接口优先于反射机制&quot; id=&quot;markdown-toc-53接口优先于反射机制&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;53&lt;/code&gt;.接口优先于反射机制&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#54谨慎地使用本地方法&quot; id=&quot;markdown-toc-54谨慎地使用本地方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;54&lt;/code&gt;.谨慎地使用本地方法&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#55谨慎地进行优化&quot; id=&quot;markdown-toc-55谨慎地进行优化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;55&lt;/code&gt;.谨慎地进行优化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#56遵守普遍接受的命名习惯&quot; id=&quot;markdown-toc-56遵守普遍接受的命名习惯&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;56&lt;/code&gt;.遵守普遍接受的命名习惯&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#八-异常&quot; id=&quot;markdown-toc-八-异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;八&lt;/code&gt; 异常&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#57只针对不正常情况使用异常&quot; id=&quot;markdown-toc-57只针对不正常情况使用异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;57&lt;/code&gt;.只针对不正常情况使用异常&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#58对可恢复的情况使用受检异常对编程错误使用运行时异常&quot; id=&quot;markdown-toc-58对可恢复的情况使用受检异常对编程错误使用运行时异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;58&lt;/code&gt;.对可恢复的情况使用受检异常，对编程错误使用运行时异常&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#59避免不必要地使用受检的异常&quot; id=&quot;markdown-toc-59避免不必要地使用受检的异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;59&lt;/code&gt;.避免不必要地使用受检的异常&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#60优先使用标准的异常&quot; id=&quot;markdown-toc-60优先使用标准的异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;60&lt;/code&gt;.优先使用标准的异常&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#61抛出与抽象相对应的异常&quot; id=&quot;markdown-toc-61抛出与抽象相对应的异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;61&lt;/code&gt;.抛出与抽象相对应的异常&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#62每个方法抛出的异常都要有文档&quot; id=&quot;markdown-toc-62每个方法抛出的异常都要有文档&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;62&lt;/code&gt;.每个方法抛出的异常都要有文档&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#63在细节消息中包含能捕捉失败的信息&quot; id=&quot;markdown-toc-63在细节消息中包含能捕捉失败的信息&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;63&lt;/code&gt;.在细节消息中包含能捕捉失败的信息&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#64努力使失败保持原子性&quot; id=&quot;markdown-toc-64努力使失败保持原子性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;64&lt;/code&gt;.努力使失败保持原子性&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#65不要忽略异常&quot; id=&quot;markdown-toc-65不要忽略异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;65&lt;/code&gt;.不要忽略异常&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#九-并发&quot; id=&quot;markdown-toc-九-并发&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;九&lt;/code&gt; 并发&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#66同步访问共享的可变数据&quot; id=&quot;markdown-toc-66同步访问共享的可变数据&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;66&lt;/code&gt;.同步访问共享的可变数据&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#67避免过度同步&quot; id=&quot;markdown-toc-67避免过度同步&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;67&lt;/code&gt;.避免过度同步&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#68executor和task优先于线程&quot; id=&quot;markdown-toc-68executor和task优先于线程&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;68&lt;/code&gt;.executor和task优先于线程&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#69并发工具优先于wait和notify&quot; id=&quot;markdown-toc-69并发工具优先于wait和notify&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;69&lt;/code&gt;.并发工具优先于wait和notify&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#70线程安全性的文档化&quot; id=&quot;markdown-toc-70线程安全性的文档化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;70&lt;/code&gt;.线程安全性的文档化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#71慎用延迟初始化&quot; id=&quot;markdown-toc-71慎用延迟初始化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;71&lt;/code&gt;.慎用延迟初始化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#72不要依赖于线程调度器&quot; id=&quot;markdown-toc-72不要依赖于线程调度器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;72&lt;/code&gt;.不要依赖于线程调度器&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#73避免使用线程组&quot; id=&quot;markdown-toc-73避免使用线程组&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;73&lt;/code&gt;.避免使用线程组&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#十-序列化&quot; id=&quot;markdown-toc-十-序列化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;十&lt;/code&gt; 序列化&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#74谨慎地实现serializable接口&quot; id=&quot;markdown-toc-74谨慎地实现serializable接口&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;74&lt;/code&gt;.谨慎地实现Serializable接口&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#75考虑使用自定义的序列化形式&quot; id=&quot;markdown-toc-75考虑使用自定义的序列化形式&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;75&lt;/code&gt;.考虑使用自定义的序列化形式&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#76保护性地编写readobject方法&quot; id=&quot;markdown-toc-76保护性地编写readobject方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;76&lt;/code&gt;.保护性地编写readObject方法&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#77对于实例的控制枚举类型优先于readresolve&quot; id=&quot;markdown-toc-77对于实例的控制枚举类型优先于readresolve&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;77&lt;/code&gt;.对于实例的控制，枚举类型优先于readResolve&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#78考虑用序列化代理代替序列化实例&quot; id=&quot;markdown-toc-78考虑用序列化代理代替序列化实例&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;78&lt;/code&gt;.考虑用序列化代理代替序列化实例&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;一-创建和销毁对象&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;一&lt;/code&gt; 创建和销毁对象&lt;/h1&gt;

&lt;h2 id=&quot;1考虑用静态工厂方法代替构造器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.考虑用静态工厂方法代替构造器&lt;/h2&gt;

&lt;p&gt;静态工厂方法的优势：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.有名称，可以见名知义了解获取对象的特点&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.不必每次调用时都创建一个对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.可以返回原类型的任何子类型对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.创建参数化类型实例时，可以使代码更简洁(右边无需再写一遍)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.不可变对象可以进行缓存，以提升性能&lt;/p&gt;

&lt;h2 id=&quot;2遇到多个构造器参数时要考虑用构建器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.遇到多个构造器参数时要考虑用构建器&lt;/h2&gt;

&lt;p&gt;构建器优势：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.重叠构造器代码冗余&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JavaBean&lt;/code&gt;模式阻止了将类做成不可变的可能&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builder&lt;/code&gt;可以进行参数检查&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.无论创建多少个对象，builder只需要一个，且可以在创建不同对象时进行调整，或者自动填充某些域 。&lt;/p&gt;

&lt;p&gt;不足：创建对象前需要先创建builder&lt;/p&gt;

&lt;h2 id=&quot;3用私有构造器或者枚举类型强化singleton属性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.用私有构造器或者枚举类型强化Singleton属性&lt;/h2&gt;

&lt;p&gt;不使用枚举时：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.构造器私有&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.构造器抛出异常 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if(INSTANCE!=null) throw&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.序列化时必须声明所有实例域都是瞬时（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transient&lt;/code&gt;）的，并提供一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readResolve&lt;/code&gt;方法返回现有的单例&lt;/p&gt;

&lt;h2 id=&quot;4通过私有构造器强化不可实例化的能力&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.通过私有构造器强化不可实例化的能力&lt;/h2&gt;

&lt;p&gt;并在构造器中抛出异常&lt;/p&gt;

&lt;h2 id=&quot;5避免创建不必要的对象&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.避免创建不必要的对象&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果对象是不可变的,将始终可以重用&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.对于给定对象的适配器,不需要创建多个实例,例如:对于同一Map对象的keySet方法,每次返回的都是同一个对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.优先使用基本类型,而不是装箱类型&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.通过维护对象池来避免创建对象并不是好主意,除非对象非常昂贵(数据库连接池).&lt;/p&gt;

&lt;h2 id=&quot;6消除过期的对象引用&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.消除过期的对象引用&lt;/h2&gt;

&lt;p&gt;内存泄露常见来源：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.无意识的对象保持(只要是类自己管理内存，程序员就应该警惕内存泄露问题)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.被遗忘在缓存中的对象引用(考虑用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WeakHashMap&lt;/code&gt;实现缓存)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.监听器和其他回调没有显式地取消注册&lt;/p&gt;

&lt;p&gt;4种引用：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;strong&gt;强引用&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object obj = new Object();&lt;/code&gt;： 只要强引用还存在，GC永远不会回收&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.&lt;strong&gt;软引用&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SoftReference&lt;/code&gt;：用来描述一些有用但不必需的对象，在系统将要发生内存溢出之前，GC会对软引用对象进行回收，若回收后仍没有足够内存，才会抛出异常。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.&lt;strong&gt;弱引用&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WeakReference&lt;/code&gt;：也是用来描述非必需对象的，但是强度比软引用更弱，弱引用对象只能生存到下一次GC发生之前，当GC工作时，无论内存是否足够，都会回收弱引用对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.&lt;strong&gt;虚引用&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PhantomReference&lt;/code&gt;：也称为幽灵引用或幻影引用，是最弱的一种引用关系。一个对象是否有虚引用存在，完全不会对其生存时间构成影响，也无法通过虚引用来获得一个实例。 为对象设置一个虚引用的唯一目的就是能在这个对象被GC回收时收到一个系统通知。&lt;/p&gt;

&lt;h2 id=&quot;7避免使用终结方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.避免使用终结方法&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.终结方法不能保证会被及时执行，也不能保证会被执行，所以不应该依赖终结方法来更新重要的持久状态&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.终结方法中发生的异常不会使线程终止，也不会打印栈轨迹，对象将处于破坏状态&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.终结方法有非常严重的性能损失&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果需要终止资源，应该显式地提供终止方法(io中的close方法)，通常与try-finally结合使用，以确保及时终止&lt;/p&gt;

&lt;p&gt;注意：&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.除非作为安全网或者终止非关键资源，否则不要使用终结方法，最好就当没这个方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果使用了终结方法，要记住&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super.finalize&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果终结方法作为安全网，要记录终结方法的非法用法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果终结方法所属的类不是final的，请考虑使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;终结方法守卫者&lt;/code&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;二-通用方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;二&lt;/code&gt; 通用方法&lt;/h1&gt;

&lt;h2 id=&quot;8覆盖equals时请遵守通用约定&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.覆盖equals时请遵守通用约定&lt;/h2&gt;

&lt;p&gt;必须遵守&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;自反性&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;对称性&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;传递性&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;一致性&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;非空性&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;可以用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt;连接所有对field的比较结果使代码简洁&lt;/p&gt;

&lt;p&gt;诀窍:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;==&lt;/code&gt; 操作符比较以优化性能&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instanceof&lt;/code&gt;检查类型是否正确可以同时搞定null判断&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.只比较关键域,可以通过关键域计算出来的冗余域不需要比较&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.优先比较最可能不同、开销最低的域&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.对&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;double&lt;/code&gt;用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compare&lt;/code&gt;特殊处理，对&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;数组&lt;/code&gt;用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Arrays.equals&lt;/code&gt;处理&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.覆盖equals时总要覆盖hashCode&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.不要企图让equals太智能，不需要考虑参数可能存在的等价关系&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.不要修改equals方法的参数类型(修改了就不是覆盖了),@Override可以避免这一点&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.&lt;strong&gt;里氏替换原则&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;.优先用&lt;strong&gt;组合+公有视图&lt;/strong&gt;的方式扩展值组件，而不是用继承&lt;/p&gt;

&lt;h2 id=&quot;9覆盖equals时总要覆盖hashcode&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.覆盖equals时总要覆盖hashCode&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.相同的对象必须具有相同的hashCode(在hash表中,hashCode用来定位hash bucket,如果相等的对象有不同的hashCode,那么hash表将从错误的hash bucket中寻找对象,最后会判定为不存在)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;result = 31 * result + c&lt;/code&gt;的方式获取hashCode，result初值设为17，c为每个域的散列值&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.能够通过关键域计算出来的&lt;strong&gt;冗余域&lt;/strong&gt;可以排除&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果计算hashCode开销很大，可以对hashCode进行缓存，并在hashCode方法第一次被访问时才进行计算&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.不要试图排除关键域(equals用到的所有域)来提高性能，这样虽然会提高hashCode计算速度，但是会提高hashCode重复率，反而降低散列性能。&lt;/p&gt;

&lt;h2 id=&quot;10始终要覆盖tostring&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;.始终要覆盖toString&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.优点：易于阅读，易于调试，易与其他应用交换数据&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.提供一个静态工厂用于将toString返回值转换为本身的类型&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.在文档中明确表示是否规定了toString的格式，如果规定了格式就要严格遵守&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.对toString返回值中包含的关键内容提供访问方法&lt;/p&gt;

&lt;h2 id=&quot;11谨慎地覆盖clone&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11&lt;/code&gt;.谨慎地覆盖clone&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.实现Cloneable接口(空接口)需要提供一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public clone&lt;/code&gt;(Object中声明为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proteced&lt;/code&gt;)方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.从jdk1.5开始，引入了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;协变返回类型&lt;/code&gt;，clone方法被覆盖后可以返回Object的子类型（通则：永远不要让客户去做任何类库能完成的事情）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.clone方法就是另一个构造器；必须确保它不会伤害到原始对象，并确保正确地创建被克隆对象中的约束条件。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.clone架构与引用可变对象的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;final&lt;/code&gt;域的正常用法是不相兼容的，为了使类可clone，有时需要去掉一些final&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;深拷贝&lt;/code&gt;，递归地调用每个域中包含的域，一直深入到不可变类为止&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.拷贝”长”对象(数组，链表等)时，如果使用&lt;strong&gt;递归&lt;/strong&gt;会创建很多指针消耗栈空间，可以考虑换成&lt;strong&gt;迭代&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.另一种方法：先调用super.clone()，将所有域设为0值，然后为其赋值新对象。这种方法简单合理，但是没有”直接操作对象及其域中对象的clone方法“快。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.clone过程中不应该调用任何非final方法(如果是private方法则不需要final)，因为子类有机会改变该方法的行为。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.重写的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public clone&lt;/code&gt;方法应该忽略&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CloneNotSupportedException&lt;/code&gt;，但如果&lt;/p&gt;

&lt;p&gt;该类是用来被继承的，则应该模拟Object.clone：声明为protected，抛出异常，不实现Cloneable。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;.对于不可变类，提供clone没有太大意义&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11&lt;/code&gt;.用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;拷贝构造器&lt;/code&gt;(即参数类型为类本身的构造器)或者&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;拷贝工厂&lt;/code&gt;代替clone，可以增加一个参数实现克隆对象的转型（例如接受一个Collection参数与一个Class参数）&lt;/p&gt;

&lt;h2 id=&quot;12考虑实现comparable接口&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12&lt;/code&gt;.考虑实现Comparable接口&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.可以方便地使用各种泛型算法与Comparable的集合实现&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.强烈建议&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(x.compareTo(y) == 0) == (x.equals(y))&lt;/code&gt;，但非必须，若违反了这个条件，应该予以说明。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.不满足上一条约定的对象放入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;有序集合&lt;/code&gt;(使用compareTo，如TreeSet)中后，将无法遵守集合接口通用(使用equals)约定（如BigDecimal，浮点数）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.优先用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;组合+公有视图&lt;/code&gt;的方式扩展值组件，而不是用继承(同equals)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.Comparable只规定了符号，没有规定数值大小，可以直接返回两个数值相减的结果，但是要小心溢出&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;三-类和接口&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;三&lt;/code&gt; 类和接口&lt;/h1&gt;

&lt;h2 id=&quot;13使类和成员的可访问性最小化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;13&lt;/code&gt;.使类和成员的可访问性最小化&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.实例域决不能是公有的&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.静态公共域只能为final修饰的基本类型或者final修饰的不可变类&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.unmodifiableList(arr)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;将数组变成不可变列表后再提供访问；或者在访问方法中返回数组的clone()&lt;/p&gt;

&lt;h2 id=&quot;14在公有类中使用访问方法而非公有域&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;14&lt;/code&gt;.在公有类中使用访问方法而非公有域&lt;/h2&gt;

&lt;p&gt;暴露域将失去内部表示法的灵活性，也将失去对数据访问进行限制的能力&lt;/p&gt;

&lt;h2 id=&quot;15使可变性最小化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15&lt;/code&gt;.使可变性最小化&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.创建不可变类的5条规则&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1)&lt;/code&gt;.不要提供任何会修改对象状态的方法&lt;/p&gt;

  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(2)&lt;/code&gt;.保证类不会被扩展(final或者构造器私有)&lt;/p&gt;

  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(3)&lt;/code&gt;.使所有域都是final的&lt;/p&gt;

  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(4)&lt;/code&gt;.使所有域成为私有的&lt;/p&gt;

  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(5)&lt;/code&gt;.确保对于任何可变组件的互斥访问&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.用公有的静态域或静态工厂为不可变类提供缓存&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;不可变对象本质上是线程安全的，它们不要求同步，可以被自由地共享，永远不需要保护性拷贝，不需要拷贝构造器&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.不可变类可以共享内部信息&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.不可变对象为其他对象提供了大量的构件&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.不可变类的缺点：每一个不同的值都需要一个单独的对象，存在性能问题，可以提供1个配套类来进行过渡操作(如StringBuilder)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.因历史问题没有设置成final的不可变类(如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BigInteger&lt;/code&gt;)，在使用时需要检查对象是否被扩展了，如果被扩展了就可能需要保护性拷贝&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.如果实现&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Serializable&lt;/code&gt;，必须显式地提供&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readObject&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readResolve&lt;/code&gt;方法或者使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjectOutputStream.writeUnshared&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjectInputStream.readUnshared&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.除非有必须的理由让类可变，否则类应该是不可变的；如果类无法做成不可变的，也应该尽力限制其可变性。因此域也应该是final的。&lt;/p&gt;

&lt;h2 id=&quot;16复合优先于继承&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;16&lt;/code&gt;.复合优先于继承&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.继承破坏了封装性，子类依赖于父类的实现细节，若父类内部发生变动，将可能损坏子类。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.假设子类覆盖父类所有的方法，增加了对参数的类型检查；后来父类增加了新方法，而子类没有同步更新，非法的参数将有可能通过类型检查，这将会产生安全漏洞&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.假设子类增加了新方法，后来父类也新增了1个签名相同但返回类型不同的方法，编译将不通过；如果方法签名与返回类型都相同，将形成覆盖，又回到了上一种情况&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;复合/转发&lt;/code&gt;的类称为组件，并实现相同接口的类为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;包装类&lt;/code&gt;，每个接口只需要1个转发类&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.包装类不适用于回调框架，回调函数传递是1个指向自身的引用，避开了外面的包装类&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.继承会将api中所有缺陷传播到子类中去，而复合却可以设计新的api隐藏缺陷&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.只有真正存在从属关系时，才应该使用继承&lt;/p&gt;

&lt;h2 id=&quot;17要么为继承而设计并提供文档说明要么就禁止继承&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;17&lt;/code&gt;.要么为继承而设计，并提供文档说明，要么就禁止继承&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;strong&gt;决不能在构造器中调用可以被覆盖的方法&lt;/strong&gt;，否则这样的方法将会在子类构造器之前运行，若是该方法中存在对域的访问，将会造成空指针异常&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.类必须提供适当的hook，以便能够进入子类的内部工作流程&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果实现了Cloneable或Serializable接口，那么&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clone&lt;/code&gt;与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readObject&lt;/code&gt;都&lt;strong&gt;不&lt;/strong&gt;能调用可被覆盖的方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果实现了Serializable接口，必须使readObject与writeReplace成为&lt;strong&gt;受保护&lt;/strong&gt;的方法，否则它们将会被子类忽略掉&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.如果必须让类可被继承，&lt;strong&gt;务必保证这个类永远不会调用可覆盖方法&lt;/strong&gt;，可以机械地消除可覆盖方法的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;自用性&lt;/code&gt;：将每个可覆盖方法的方法体移到一个私有的“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;辅助方法&lt;/code&gt;中”，然后让可覆盖方法调用辅助方法实现功能，这样可以确保可覆盖方法永远不会干涉到父类的运行。&lt;/p&gt;

&lt;h2 id=&quot;18接口优于抽象类&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18&lt;/code&gt;.接口优于抽象类&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.现有的类易被更新，以实现新的接口&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.接口是定义mixin（混合类型）理想选择&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.接口允许我们构造非层次接口的类型框架&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.在包装类模式下，接口使得安全地增强类的功能成为可能&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.通过对导出的每个接口都提供一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;骨架实现&lt;/code&gt;（skeletal implementation）类，把接口和抽象类的优点结合起来&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.实现了接口的类可以把对于接口方法的调用转发到一个内部私有类的实例上，这个内部私有类扩展了骨架实现类，这种方法被称为“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;多重模拟继承&lt;/code&gt;”。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.抽象类比接口更易于扩展，抽象类可以直接增加方法，接口却不可以。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.接口一旦被公开发行，并且已被广泛实现，再想改变接口是不可能的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.接口通常是定义允许多个实现的类型的最佳途径，但是当&lt;strong&gt;演变&lt;/strong&gt;的容易性比&lt;strong&gt;灵活性&lt;/strong&gt;和&lt;strong&gt;功能性&lt;/strong&gt;更重要时例外。&lt;/p&gt;

&lt;h2 id=&quot;19接口只用于定义类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;19&lt;/code&gt;.接口只用于定义类型&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.常量接口模式是对接口的不良使用&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果这些常量最好被看作枚举类型的成员，就应该是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;枚举类型&lt;/code&gt;，否则应该使用不可实例化的工具类来导出这些常量。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果大量利用工具类来导出常量，可以通过利用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;静态导入&lt;/code&gt;机制，避免用类名来修饰常量名。（1.5版本后）&lt;/p&gt;

&lt;h2 id=&quot;20类层次优于标签类&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;20&lt;/code&gt;.类层次优于标签类&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.标签类过于冗长，容易出错，并且效率低下&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.编译器确保每个类的构造器都初始化它的数据域，对于根类中声明的每个抽象方法，都确保有一个实现。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.类层次的另一种好处在于，它们可以用来反映类型之间本质上的层次关系，有助于增强灵活性，并进行更好的编译时类型检查。&lt;/p&gt;

&lt;h2 id=&quot;21用函数对象表示策略&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;21&lt;/code&gt;.用函数对象表示策略&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果一个类仅仅导出一个方法，它的实例实际上就等同于一个指向该方法的指针。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.设计具体的策略类时，还需要定义一个策略接口&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果它被重复执行，考虑将函数对象存储到一个私有的静态final域里，并重用它。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.宿主类可以导出公有的静态域（或静态工厂方法），其类型为策略接口，具体的策略类可以是宿主类的私有嵌套类。&lt;/p&gt;

&lt;h2 id=&quot;22优先考虑静态成员类&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;22&lt;/code&gt;.优先考虑静态成员类&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.嵌套类存在的目的应该只是为它的外围类提供服务。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.嵌套类有四种：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;静态成员类&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;非静态成员类&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;匿名类&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;局部类&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.非静态成员类每个实例都隐含着与外围类的一个实例相关联。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果嵌套类的实例可以在它的外围类的实例之外独立存在，这个嵌套类就必须是静态成员类；在没有外围实例的情况下，想创建非静态成员类的实例是不可能的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.非静态成员类的一种常见用法是定义一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Adapter&lt;/code&gt;，它允许外部类的实例被看作是另一个不相关的类的实例。（如集合视图 keySet iterator）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.如果声明成员类不要求访问外围实例，那成员类就应该是静态的。非静态成员类的实例必须要有一个外围的实例&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.私有静态成员类的一种常见用法是用来代表外围类所代表的对象的组件。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.匿名类的常见用法是①动态地创建函数对象（第21条），②创建对象过程（Runabel,Thead,TimerTask）,③在静态工厂方法的内部&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.在任何可以声明局部变量的地方，都可以声明局部类，并且局部类也遵守同样的作用域规则。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;四-泛型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;四&lt;/code&gt; 泛型&lt;/h1&gt;

&lt;h2 id=&quot;23不要在新代码中使用原生态类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;23&lt;/code&gt;.不要在新代码中使用原生态类型&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.错误应该尽早发现，最好在编译期就发现。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果使用原生类型，就失去了泛型在安全性和表述性方面的所有优势&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.在不在乎集合中的元素类型时，使用&lt;strong&gt;无限制的通配符&lt;/strong&gt;类型（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;?&amp;gt;&lt;/code&gt;）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.在类文字中必须使用原生态类型；因为泛型会被擦除，所以&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instanceof&lt;/code&gt;操作符使用原生类型就可以了。&lt;/p&gt;

&lt;h2 id=&quot;24消除非受检警告&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24&lt;/code&gt;.消除非受检警告&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.泛型相关的编译警告：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;非受检强制转化警告（unchecked cast warnnings）&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;非受检方法调用警告&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;非受检普通数组创建警告&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;非受检转换警告（unchecked conversion warnings）&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.要尽可能地消除每一个非受检警告&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果无法消除警告，可以同时证明引起警告的代码是类型安全的，只有在这种情况下才可以用一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@SuppressWarnings(&quot;unchecked&quot;)&lt;/code&gt;注解来禁止这条警告。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.SuppressWarnings注解可以用在任何粒度的级别中，应该始终在尽可能小的范围中使用它，永远不要在整个类中使用它&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.如果在长度不止一行（即一行代码中存在多个动作）的代码中使用了SuppressWarnings注解，可以将它移到一个局部变量声明中，以减小SuppressWarnings注解的影响范围。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.每当使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@SuppressWarnings(&quot;unchecked&quot;)&lt;/code&gt;时，都要添加一条注释，说明为什么这么做是安全的。&lt;/p&gt;

&lt;h2 id=&quot;25列表优先于数组&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;25&lt;/code&gt;.列表优先于数组&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.数组与泛型的区别&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;数组是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;协变&lt;/code&gt;的，如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt;的子类型，那么&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub[]&lt;/code&gt;也是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super[]&lt;/code&gt;的子类型；相反，泛型则是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;不可变&lt;/code&gt;的，对于两个类型&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Type1&lt;/code&gt;与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Type2&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;Type1&amp;gt;&lt;/code&gt;与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;Type2&amp;gt;&lt;/code&gt;没有任何关系。&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;数组是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;具体化&lt;/code&gt;的，因此数组在运行时才知道并检查它的元素类型约束；而泛型则是通过擦除（erasure）来实现的，泛型只在编译时强化类型信息，并在运行时擦除元素类型信息，擦除可以使泛型与早期无泛型的代码兼容。&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.泛型与数组不能混合使用，因为泛型数组不是类型安全的。（因为数组是协变的，所以数组可以自动向上转型，我们可以往向上转型后的数组中插入与转型前不同的类型的数据，而借转型前数组名义取出的对象将会产生CalssCastException）&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt;[] stringLists = new List&amp;lt;String&amp;gt;[];
List&amp;lt;Integer&amp;gt; intList = Arrays.asList(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.每当调用可变参数方法时，就会创建一个数组来存放参数，如果这个数组的元素类型不是可具体化的，就会得到一条警告，除了禁止这个警告，别无他法。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.数组提供了运行时的类型安全，但是没有提供编译时的类型安全；泛型提供了编译时的类型安全，但是没有提供运行时的类型安全。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.不可具体化的类型数组转换只能在特殊情况下使用。&lt;/p&gt;

&lt;h2 id=&quot;26优先考虑泛型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;26&lt;/code&gt;.优先考虑泛型&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.禁止数组类型的未受检转换比禁止标量类型的更加危险&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.使用泛型比使用需要在客户端代码中进行转换的类型更加安全，也更加容易，在设计新类型的时候，要确保不需要这种转换就能使用。&lt;/p&gt;

&lt;h2 id=&quot;27优先考虑泛型方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;27&lt;/code&gt;.优先考虑泛型方法&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果泛型方法返回的对象是无状态的，可以考虑将该对象做成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;泛型单例&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.调用泛型方法时，编译器会对返回值进行类型推到，以适应接收者的类型&lt;/p&gt;

&lt;h2 id=&quot;28利用有限通配符来提升api的灵活性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;28&lt;/code&gt;.利用有限通配符来提升API的灵活性&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.泛型中使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extends&lt;/code&gt;时，每个类型都是自身的子类型；使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt;时，每个类型都是自身的超类型。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.为了获得最大限度的灵活性，要在表示生产者或者消费者的输入参数上使用通配符类型。&lt;strong&gt;PECS&lt;/strong&gt;（producer-extends；consumer-super）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.不要用通配符类型作为返回类型&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果编译器不能推断你希望它拥有的类型，可以通过显式的类型参数来指定。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.如果类型参数在方法中声明中只出现一次，可以用通配符取代它。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.不能把null之外的任何值放进List&amp;lt;?&amp;gt;中&lt;/p&gt;

&lt;h2 id=&quot;29优先考虑类型安全的异构容器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;29&lt;/code&gt;.优先考虑类型安全的异构容器&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cast&lt;/code&gt;方法是java的cast操作符的动态模拟，它只检验它的参数是否为Class对象所表示的类型的实例，如果是，就返回参数，否则抛出ClassCastException&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.类型安全的异构容器的缺陷&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;恶意的客户端可以很轻松的破坏类型安全，只要它以原生态类型使用Class对象，在容器的添加方法第一行加入参数的类型检查（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type.cast(obj)&lt;/code&gt;）可以避免这种问题。&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;不可存放不可具体化的类型数据（如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;String&amp;gt;&lt;/code&gt;，因为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;String&amp;gt;&lt;/code&gt;的类型为List.class，而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;String&amp;gt;.class&lt;/code&gt;是语法错误），只能保存String或String[]等可具体化的对象。&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.利用有限制类型参数或者有限制通配符来限制可以表示的类型&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asSubclass(List.class)&lt;/code&gt;方法将Class类的类型令牌安全且动态地转换成参数所表示的类(List)的一个子类的通配符表示法(? extends List)，参数必须为调用者的父类型，如果成功，返回它的参数，否则抛出ClassCastExcpetion&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;五-枚举和注解&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;五&lt;/code&gt; 枚举和注解&lt;/h1&gt;

&lt;h2 id=&quot;30用enum代替int常量&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;30&lt;/code&gt;.用enum代替int常量&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.因为没有可以访问的构造器，枚举类型是真正的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;final&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.枚举类型允许添加任意的方法和域，并实现任意的接口。枚举提供了所有Object方法的高级实现，实现了Comparable和Serializable接口，并针对枚举类型的可任意改变性设计了序列化方式。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.枚举有一个静态的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;values&lt;/code&gt;方法，按照声明顺序返回它的值数组，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toString&lt;/code&gt;方法返回每个枚举值的声明名称。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.在同一个枚举类型中，将特定的行为与每个具体的枚举常量关联起来：在枚举类型中声明一个抽象的方法，并&lt;strong&gt;在特定于常量的类主体中，覆盖该方法&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.每一个枚举常量可以关联特定的数据（在构造器中绑定），因此特定于枚举常量的方法可以与特定于枚举常量的数据结合起来。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.枚举类型有一个自动产生的valueOf(string)方法，它将常量的名字转变成常量本身。如果枚举类型中覆盖toString，要考虑编写一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fromString&lt;/code&gt;方法，将定制的字符串表示法变回相应的枚举。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.如果多个枚举常量同时共享多个行为，要考虑&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;策略枚举&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;31用实例域代替序数&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;31&lt;/code&gt;.用实例域代替序数&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.所有枚举都有一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ordinal&lt;/code&gt;方法，它返回每个枚举常量在类型中的数字位置&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.永远不要根据枚举的序数导出与他关联的值，而是要将值保存在一个实例域中。&lt;/p&gt;

&lt;h2 id=&quot;32用enumset代替位域&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;32&lt;/code&gt;.用EnumSet代替位域&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;or&lt;/code&gt;位运算将几个常量合并到一个集合中，称为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;位域&lt;/code&gt;。（形如111→rwx）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.EnumSet可以表示从单个枚举类型中提取的多个值的集合，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnumSet.of&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.截止jdk1.6，无法创建不可变的EnumSet&lt;/p&gt;

&lt;h2 id=&quot;33用enummap代替序数索引&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;33&lt;/code&gt;.用EnumMap代替序数索引&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnumMap&lt;/code&gt;构造器采用键类型的Class对象：这是一个有限制的类型令牌，它提供了运行时的泛型信息。实例化需要指定Class类型，可以保存同一个类型的对象与值的映射关系&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.最好不要用序数来索引数组，而要用EnumMap，如果需要表示的关系是多维的，就用嵌套的EnumMap&lt;/p&gt;

&lt;h2 id=&quot;34用接口模拟可伸缩的枚举&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;34&lt;/code&gt;.用接口模拟可伸缩的枚举&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.用接口模拟可伸缩的枚举有个小小的不足，即无法实现从一个枚举类型继承到另一个枚举类型。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.虽然无法编写可扩展的枚举类型，却可以通过编写接口以及实现该接口的基础枚举类型，模拟可伸缩的枚举。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;T extends Enum&amp;lt;T&amp;gt; &amp;amp; interfaceType&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;35注解优先于命名模式&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;35&lt;/code&gt;.注解优先于命名模式&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.注解的声明类型就是自身通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Retention&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Target&lt;/code&gt; 注解进行注解，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Retention(RetentionPolicy.RUNTIME)&lt;/code&gt;元注解表明，应该在运行时保留；&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Target(ElementType.METHOD)&lt;/code&gt;元注解表明，只有在方法声明中才是合法的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvocationTargetException&lt;/code&gt; 封装了目标方法在运行时产生的异常，出现该异常表明目标方法确实被运行了，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getCause()&lt;/code&gt;可以获得方法运行时产生的具体异常。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.用花括号包裹用逗号分隔的参数将自动变成参数数组&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果编写的工具需要在源文件上添加信息，就要定义一组适当的注解类型。&lt;/p&gt;

&lt;h2 id=&quot;36坚持使用override注解&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;36&lt;/code&gt;.坚持使用Override注解&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Override&lt;/code&gt;会对标记的方法进行代码检查，确保该方法成功地被重写了。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.现代IDE在检测到Override注解时会提醒用户小心无意识的覆盖。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.在抽象类或接口中使用Override可以确保不会添加新的方法。&lt;/p&gt;

&lt;h2 id=&quot;37用标记接口定义类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;37&lt;/code&gt;.用标记接口定义类型&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.标记接口是没有包含方法声明的接口，只是指明了实现类应该具有的属性。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.标记接口有两点胜过注解&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;标记接口可以在编译时进行类型检查，保证参数的有效性&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;标记接口类型更容易指定实现类的约束条件，如Set就是一个标记接口，他限制了实现类必须具有Collection特性。&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.注解有两点胜过标记接口&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;注解可以在内部丰富信息而不影响客户端使用&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;标记注解在支持注解的框架中具有一致性&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果标记需要应用到任何程序元素而不仅仅是类或接口上，就应该使用注解；否则应该使用标记接口；如果该标记只用于特定的接口，最好将标记定义成该接口的子接口(Set与Collection)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.如果一个标记永远不会扩展，应该定义成标记接口；如果未来可能需要在标记上添加更多信息，则应该定义成标记注解。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;六-方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;六&lt;/code&gt; 方法&lt;/h1&gt;

&lt;h2 id=&quot;38检查参数的有效性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;38&lt;/code&gt;.检查参数的有效性&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.应该在发生错误后，能够尽快检测出错误，不要让错误在过深的地方爆发出来。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.对于公有的方法，要用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@throws&lt;/code&gt;标签在文档中说明违反参数值限制时会抛出的异常。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.非公有的方法通常应该使用断言来检查参数， &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ea&lt;/code&gt;启用&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果有效性检查非常昂贵，或者已经隐含在计算中完成则无需进行检查&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;异常转译&lt;/code&gt;技术，将计算过程中抛出的异常转换为正确的异常。&lt;/p&gt;

&lt;h2 id=&quot;39必要时进行保护性拷贝&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;39&lt;/code&gt;.必要时进行保护性拷贝&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.对构造器的每个可变参数进行保护性拷贝&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.保护性拷贝是在检查参数的有效性之&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;前&lt;/code&gt;进行的，并且有效性检查是针对拷贝之&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;后&lt;/code&gt;的对象，而不是针对原始对象，这样可以避免在拷贝阶段从另一个线程改变类的参数。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.对于参数类型可以被不信任方子类化的参数，不要使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clone&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果需要访问类的内部域，返回内部域的保护性拷贝。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.如果不能容忍客户端传入的对象可变，使用保护性拷贝&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.长度非0的数组总是可变的，使用保护性拷贝或者返回数组的不可变视图&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.通常使用Date.getTime()返回的long类型作为内部的时间表示法。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.包装类一般客户端自己使用，无需使用保护性拷贝。&lt;/p&gt;

&lt;h2 id=&quot;40谨慎设计方法签名&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;40&lt;/code&gt;.谨慎设计方法签名&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.谨慎地选择方法的名称&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.不要过于追求提供便利的方法，只用某项操作被经常用到时，才提供快捷方式。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.避免过长的参数列表，相同类型的长参数序列格外有害。&lt;/p&gt;

&lt;p&gt;缩短参数列表的三种方法&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;把方法分解成多个方法，但是会导致方法过多，但是提升方法的正交性，可以减少方法数目。&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;创建辅助类，用来保存参数的分组。&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;从对象构建到方法调用都采用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Builder&lt;/code&gt;模式&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.对于参数类型，优先使用接口而不是类。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.对于boolean参数，要优先使用连个元素的枚举类型。&lt;/p&gt;

&lt;h2 id=&quot;41慎用重载&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;41&lt;/code&gt;.慎用重载&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.要调用哪个重载方法是在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;编译&lt;/code&gt;时做出决定的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.对于重载方法的选择是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;静态&lt;/code&gt;的，而对于被覆盖的方法的选择是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;动态&lt;/code&gt;的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果普通用户根本不知道”对于一组给定的参数，其中哪个重载方法将会调用”，那么这样的API很可能导致错误。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.安全而保守的策略是，永远不要导出两个具有相同参数数目的重载方法；如果方法使用可变参数，保守的策略是根本不重载它。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.一个类的多个构造器总是重载的，很多情况下可以用静态工厂代替构造器。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.对于一对重载方法，如果至少有一个对应的参数在两个方法中具有”&lt;strong&gt;根本不同&lt;/strong&gt;（无法强转）”的类型，那就不会让用户感到混淆。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list.remove()&lt;/code&gt;参数转换成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Integer&lt;/code&gt;，迫使选择正确的重载方法。&lt;strong&gt;java语言添加了自动装箱后，破坏了List接口&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.当传递的参数没有本质的不同时，必须确保所有的重载方法行为一致，做法是让更具体化的重载方法把调用转发给更一般化的重载方法。&lt;/p&gt;

&lt;h2 id=&quot;42慎用可变参数&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;42&lt;/code&gt;.慎用可变参数&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.参数数量检测无法在编译期完成&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.可变参数方法会将多个参数收集成&lt;strong&gt;数组&lt;/strong&gt;，但若参数本身就是数组，那么收集后的数组将无法得到预期的结果（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Arrays.asList&lt;/code&gt;）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.不必改造具有数组参数的每个方法；只当确实是在数量不定的值上执行调用时才使用可变参数&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.重视性能时要小心，可变参数方法每次调用都会进行数组分配和初始化&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.提供大多数使用情况下的不可变参数方法，再额外附加可变参数方法（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnumSet.of&lt;/code&gt;）&lt;/p&gt;

&lt;h2 id=&quot;43返回零长度的数组或者集合而不是null&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;43&lt;/code&gt;.返回零长度的数组或者集合，而不是null&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果返回null，每次调用该方法时都会需要进行空值判断&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.长度为0的数组是不可变的，因此返回的空数组可以自由共享&lt;/p&gt;

&lt;p&gt;（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.emptySet&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.emptyList&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.emptyMap&lt;/code&gt;）&lt;/p&gt;

&lt;h2 id=&quot;44为所有导出的api元素编写文档注释&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;44&lt;/code&gt;.为所有导出的API元素编写文档注释&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.文档注释应该简洁地描述出它和客户端之间的约定&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.应该列举出这个方法的所有前提条件，后置条件和副作用&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{@code}&lt;/code&gt;会造成包裹的内容以代码片段形式呈现（即生成code标签）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{@literal}&lt;/code&gt;会自动转义包裹的内容&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.同一个类或者接口中的两个成员或者构造器，不应该具有相同的概要描述&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.不要忽视类是否是&lt;strong&gt;线程安全&lt;/strong&gt;的，以及类是否是&lt;strong&gt;可序列化&lt;/strong&gt;的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{@inheritDoc}&lt;/code&gt;继承注释&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;七-通用程序设计&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;七&lt;/code&gt; 通用程序设计&lt;/h1&gt;

&lt;h2 id=&quot;45将局部变量的作用域最小化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;45&lt;/code&gt;.将局部变量的作用域最小化&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.最有力的方法就是在第一次使用它的地方声明&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.几乎每个局部变量都应该包含一个初始化表达式，如果没有足够的信息来进行有意义的初始化，就推迟这个声明，try-catch除外。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.循环中提供了特殊的机会来将变量的作用域最小化，如果在循环终止后不再需要循环变量的内容，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt;循环就优先于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while&lt;/code&gt;循环。&lt;/p&gt;

&lt;h2 id=&quot;46for-each-循环优先于传统for循环&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;46&lt;/code&gt;.for-each 循环优先于传统for循环&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.嵌套的for-each循环，外层循环的对象可以在内层对象中保持，不会随内层循环移动指针。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.有3种情况无法使用for-each&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;需要遍历并删除元素时&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;需要遍历元素并取代某些值时&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;需要并行迭代多个集合时&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;47了解和使用类库&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;47&lt;/code&gt;.了解和使用类库&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.lang&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.util&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.io&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.util.concurrent&lt;/code&gt;；&lt;/p&gt;

&lt;h2 id=&quot;48如果需要精确的答案请避免使用float和double&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;48&lt;/code&gt;.如果需要精确的答案，请避免使用float和double&lt;/h2&gt;

&lt;p&gt;使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BigDecimal&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; 或者 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;long&lt;/code&gt; 进行货币计算&lt;/p&gt;

&lt;h2 id=&quot;49基本类型优先于装箱基本类型&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;49&lt;/code&gt;.基本类型优先于装箱基本类型&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.基本类型与装箱基本类型的区别&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;基本类型只有值，而装箱基本类型的&lt;strong&gt;同一性&lt;/strong&gt;不仅取决于值&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;装箱基本类型除了值还多了一个null&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;基本类型更节省空间&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.对装箱基本类型使用==操作符几乎总是错误的。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.当一项操作中混合使用基本类型和装箱基本类型时，装箱基本类型会&lt;strong&gt;自动拆箱&lt;/strong&gt;。若被拆的对象为null，将抛出异常。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.在集合中只能使用装箱基本类型&lt;/p&gt;

&lt;h2 id=&quot;50如果其他类型更适合则尽量避免使用字符串&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50&lt;/code&gt;.如果其他类型更适合，则尽量避免使用字符串&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.字符串不适合代替其他的值类型&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.字符串不适合代替枚举类型&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.字符串不适合代替聚集类型（即将多个部分拼接成字符串用关键字分隔），最好用一个简单的类来代替。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.字符串不可替代控制共享资源访问权限的键&lt;/p&gt;

&lt;h2 id=&quot;51当心字符串连接的性能&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;51&lt;/code&gt;.当心字符串连接的性能&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.为连接n个字符串而重复地使用字符串连接操作符，需要n的平方级的时间&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.为了性能，用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringBuilder&lt;/code&gt;替代String&lt;/p&gt;

&lt;h2 id=&quot;52通过接口引用对象&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;52&lt;/code&gt;.通过接口引用对象&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果有合适的接口类型存在，那么对于参数，返回值，变量和域来说，就都应该使用接口类型进行声明&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果对象属于基于类的框架，就应该使用基类引用这个对象，尽量用抽象类型引用对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果程序依赖于类中额外的方法，那么这种类应该只被用来引用实例，而不应该作为参数类型。&lt;/p&gt;

&lt;h2 id=&quot;53接口优先于反射机制&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;53&lt;/code&gt;.接口优先于反射机制&lt;/h2&gt;

&lt;p&gt;反射功能应该只有在设计时被用到，或者只是用来创建对象，程序在运行时不应该以反射方式访问对象&lt;/p&gt;

&lt;h2 id=&quot;54谨慎地使用本地方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;54&lt;/code&gt;.谨慎地使用本地方法&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.本地方法3个用途&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;提供访问特定于平台的机制的能力，如注册表和文件锁&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;提供了访问遗留代码库的能力&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;通过本地语言，编写程序中注重性能的部分&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.使用本地方法&lt;strong&gt;不值得提倡&lt;/strong&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.util.prefs&lt;/code&gt;提供了访问注册表的能力，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.awt.SystemTray&lt;/code&gt;提供了访问桌面系统托盘的能力&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.本地语言是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;不安全&lt;/code&gt;的，无法免受内存毁坏错误的影响。&lt;/p&gt;

&lt;h2 id=&quot;55谨慎地进行优化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;55&lt;/code&gt;.谨慎地进行优化&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.好的程序体现了信息隐藏的原则，只要有可能，就要把设计决策（暂时理解为业务逻辑）集中在单个模块中，因此，可以改变单个决策，而不会影响到系统的其他部分。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.努力避免那些限制性能的设计决策，系统中最难更改的是指定模块&lt;strong&gt;交互关系&lt;/strong&gt;的组件，&lt;strong&gt;API&lt;/strong&gt;，&lt;strong&gt;线路层协议&lt;/strong&gt;和&lt;strong&gt;永久数据格式&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.要考虑API设计设计决策的性能后果&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;如果将公类类型成为可变的，就可能导致大量不必要的保护性拷贝&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;在适合使用复合模式的公有类中使用继承，会把这个类与它的超类永远束缚在一起&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;在API中使用实现类而不是接口，会将客户端束缚在具体的实现类上，如果将来出现更快的实现也无法使用。&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;56遵守普遍接受的命名习惯&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;56&lt;/code&gt;.遵守普遍接受的命名习惯&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.《&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;The Java Language Specification&lt;/code&gt;》&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.对于缩写单词，也应该遵守首字母大写原则&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.常量域是唯一推荐使用下划线的情形&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.类型参数，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;任意类型，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;E&lt;/code&gt;集合元素类型，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KV&lt;/code&gt;键值映射类型，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt;异常，任何类型的序列可以是T,U,V或者T1,T2,t3&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.转换对象类型，返回不同类型的独立对象的方法 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toType&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;返回视图&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asType&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;返回一个与被调用对象相同值的基本类型的方法&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typeValue&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;静态工厂&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueOf&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;of&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getInstance&lt;/code&gt;，&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newInstance&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getType&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NewType&lt;/code&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;八-异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;八&lt;/code&gt; 异常&lt;/h1&gt;

&lt;h2 id=&quot;57只针对不正常情况使用异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;57&lt;/code&gt;.只针对不正常情况使用异常&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.异常机制的设计初衷是为了不正常的情形，很少有JVM实现会对它进行优化；把代码放在try-catch中反而阻止了本来可以进行的优化；对数组进行遍历的标准模式并会不导致冗余的检查，有些现代的JVM实现会将它们优化掉。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.异常只应用于不正常情况，不应该用于正常的控制流。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果类具有状态，有两种方法可以避免异常，第一种是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;状态测试法&lt;/code&gt;，如Iteable接口的hasNext。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.另一种方法是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;可识别返回值法&lt;/code&gt;，并发环境下或状态检查影响性能时更适用，如果所有情况等同，优先使用状态检测法。&lt;/p&gt;

&lt;h2 id=&quot;58对可恢复的情况使用受检异常对编程错误使用运行时异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;58&lt;/code&gt;.对可恢复的情况使用受检异常，对编程错误使用运行时异常&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.三种可抛出结构：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;受检异常&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;运行时异常&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;错误&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果期望调用者能够适当地恢复，使用受检的异常&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.用运行时异常表明编程错误，大多数运行时异常表示提前违例，即API用户没有遵守约定&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.错误通常被JVM保留，用于表示资源不足，约束失败等，最好不要再实现任何新的Error子类&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.如果不清楚是否有可能恢复，最好使用&lt;strong&gt;未受检&lt;/strong&gt;的异常&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.受检的异常往往需要表明可回复条件，提供一些便于查询信息的方法尤其重要&lt;/p&gt;

&lt;h2 id=&quot;59避免不必要地使用受检的异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;59&lt;/code&gt;.避免不必要地使用受检的异常&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果正确使用API不能阻止异常的产生，而一旦产生异常API使用者能够立即采取有效的动作，就值得使用受检的异常，除非两个条件都成立，否则更适合使用&lt;strong&gt;未受检&lt;/strong&gt;异常。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.把受检的异常变成未受检异常的方法是把抛出异常的方法分成两个方法，其中一个方法返回boolean表示是否应该抛出异常（类似状态检测法）。这种方法在并发环境下或者异常检测很耗性能时则不适用。&lt;/p&gt;

&lt;h2 id=&quot;60优先使用标准的异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;60&lt;/code&gt;.优先使用标准的异常&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentModificationException&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsupportedOperationException&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;61抛出与抽象相对应的异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;61&lt;/code&gt;.抛出与抽象相对应的异常&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.更高层的实现应该捕获低层的异常，同时抛出可以按照高层抽象进行解释的异常，这种做法称为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;异常转译&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.一种特殊的异常转译形式称为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;异常链&lt;/code&gt;，如果低层异常对于调试高层异常有帮助，就适合使用异常链，高层异常提供访问方法获得低层异常（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Throwable.getCause&lt;/code&gt;）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.大多数标准异常都有异常链构造器，没有异常链构造器的，可以使用Throwable的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initCause&lt;/code&gt;方法设置原因。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.异常转译不应滥用，更好的做法是&lt;strong&gt;及时进行参数检查&lt;/strong&gt;，尽早中断异常链。&lt;/p&gt;

&lt;h2 id=&quot;62每个方法抛出的异常都要有文档&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;62&lt;/code&gt;.每个方法抛出的异常都要有文档&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.始终要单独声明受检的异常，而不是抛出多个异常的共同的超类，并且利用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@throws&lt;/code&gt;标记，准确记录下抛出每个异常的条件&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.不要在方法声明中抛出任何&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimException&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果一个类中的多个方法出于同样的原因抛出同样的异常，可以将异常的信息写入到类的文档注释中。&lt;/p&gt;

&lt;h2 id=&quot;63在细节消息中包含能捕捉失败的信息&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;63&lt;/code&gt;.在细节消息中包含能捕捉失败的信息&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果捕获失败，异常的细节信息应该包含所有对异常有贡献的参数和域的值&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.在异常的&lt;strong&gt;构造器&lt;/strong&gt;中引入细节信息&lt;/p&gt;

&lt;h2 id=&quot;64努力使失败保持原子性&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;64&lt;/code&gt;.努力使失败保持原子性&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.失败的方法调用应该使对象保持在被调用之前的状态&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.保持失败原子性的方法：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;设计不可变类&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;检查参数有效性，提前失败，将所有可能产生失败的运算放到改变状态的操作之前&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;编写恢复代码&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;在对象的一份临时拷贝上操作，然后用拷贝中的结果代替对象的内容（如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.sort&lt;/code&gt;）&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.在缺乏同步机制的情况下并发访问对象，难以保持失败原子性。&lt;/p&gt;

&lt;h2 id=&quot;65不要忽略异常&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;65&lt;/code&gt;.不要忽略异常&lt;/h2&gt;

&lt;p&gt;有一种情形可以忽略异常，即关闭FileInputStream时，因为文件状态没有改变，不必终止当前操作，即使这种情况下，也应该将异常信息记录下来，以便排查。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;九-并发&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;九&lt;/code&gt; 并发&lt;/h2&gt;

&lt;h2 id=&quot;66同步访问共享的可变数据&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;66&lt;/code&gt;.同步访问共享的可变数据&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.同步可以保证每个线程看到的对象处于一致 的状态中；保证每个线程都看到锁对象被修改的结果。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.java语言规范保证，读或者写一个变量是原子的，除非这个变量的类型为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;long&lt;/code&gt;或&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;double&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.读写原子数据的时候也应该使用同步，不仅为了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;互斥访问&lt;/code&gt;，也为了在线程间进行可靠的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;通信&lt;/code&gt;。详见&lt;strong&gt;java内存模型&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.不要用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thead.sotp&lt;/code&gt;。要中断一个线程，建议做法是轮询boolean域，通过另一个线程修改这个boolean域；具体方法是添加对这个boolean域的读写方法，并对读写方法添加&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;同步&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;volatile&lt;/code&gt;修饰符虽然&lt;strong&gt;不&lt;/strong&gt;执行互斥访问，但是可以保证每个线程在读取该域的时候都能看到&lt;strong&gt;最近&lt;/strong&gt;刚被写入的值。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AtomicLong&lt;/code&gt;进行原子化long操作&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.最佳做法是要么不共享可变数据，要么就共享不可变数据，就是将可变数据限制在单个线程内部。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.活性失败：线程永远无法读取到变量的变化&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.安全性失败：一个线程修改非原子变量时，另一个线程过来读取，此时只能读取到&lt;strong&gt;过渡&lt;/strong&gt;状态的数据。&lt;/p&gt;

&lt;h2 id=&quot;67避免过度同步&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;67&lt;/code&gt;.避免过度同步&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.为了避免活性失败和安全性失败，在一个被同步的方法或者代码块中，永远不要放弃对客户端的控制。尽量不要在同步代码块中调用可被覆盖的方法或者客户端提供的方法（&lt;strong&gt;原则上永远不要信任客户端&lt;/strong&gt;）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.当客户端造成服务端变化后能够预定通知，即观察者（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Observer&lt;/code&gt;）模式&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.线程B需要获得锁才能继续执行，但是线程B执行完当前的代码片段后，线程A才能让出锁，此时将会造成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;死锁&lt;/code&gt;。死锁的根本原因是，多个线程同时需求同步锁。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.java中的锁是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;可重入&lt;/code&gt;的，这样的锁简化了多线程面向对象的构造&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.不要把锁对象传递给客户端，而是传递锁对象的快照，这样可以避免死锁&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.java类库提供了并发集合&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyOnWriteArrayList&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.同步区域内的工作越少越好&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.如果一个可变的类需要并发访问，应该使这个类变成线程安全的，通过内部同步可以实现；如果不确定是否需要同步，那就不要同步，并建立文档，注明它不是线程安全的&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.如果在内部同步了类，就可以进行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;分拆锁&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;分离锁&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;非阻塞&lt;/code&gt;并发控制&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;.如果方法修改了静态域，那么就必须同步对这个域的访问，即使它往往只作用于单个线程，这是为了可靠的线程通信&lt;/p&gt;

&lt;h2 id=&quot;68executor和task优先于线程&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;68&lt;/code&gt;.executor和task优先于线程&lt;/h2&gt;

&lt;p&gt;1.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Executor Framework&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(runnable);
executor.shutdown();
invokeAny
invokeAll
awaitTermination
ExecutorCompletionService
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果编写的是小程序，或者轻载服务器，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Executors.newCachedThreadPool&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.在大负载的产品服务器中，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Executors.newFixedThreadPool&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.尽量不要编写自己的工作队列，尽量不直接使用线程。现在的关键抽象不再是Thread了，而是task。task有两种：Runnable与Callable，执行任务的关键机制是ExecutorService。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.ScheduledThreadPool代替Timer&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.《&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Java Concurrency in Practice&lt;/code&gt;》&lt;/p&gt;

&lt;h2 id=&quot;69并发工具优先于wait和notify&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;69&lt;/code&gt;.并发工具优先于wait和notify&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.concurrent包中三类工具：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;Executor Framework&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;并发集合&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;同步器&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.并发集合中不可能排除并发活动，将他锁定没什么作用，只会使程序速度变慢。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.应该优先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentHashMap&lt;/code&gt;，而不是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collections.synchronizedMap&lt;/code&gt;或者&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hashtable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.阻塞队列用于工作队列或&lt;strong&gt;生产者-消费者&lt;/strong&gt;队列，任意个生产者线程向工作队列中添加项目，并且当工作项目可用时，任意个消费者线程从工作队列中取出项目&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.同步器是一些使线程能够等待另一个线程的对象（互斥），最常用的同步器是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountDownLatch&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt;，较不常用的是CyclicBarrier和Exchanger&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.倒计数锁存器，当countDown数量足够时，将自动从&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt;状态唤醒，否则将继续等待。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.线程饥饿死锁&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.对于间歇式的定时，始终应该优先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.nanoTime&lt;/code&gt;，而不是System.currentTimeMills&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.wait()&lt;/code&gt;，使所有访问a对象的线程等待，该方法必须在同步区域内被调用；始终应该使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait循环模式&lt;/code&gt;调用wati方法，永远不要在循环之外调用wait方法。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;.优先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifyAll&lt;/code&gt;，因为被意外唤醒的线程会在循环中检查条件，如果条件不满足，会继续等待。&lt;/p&gt;

&lt;h2 id=&quot;70线程安全性的文档化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;70&lt;/code&gt;.线程安全性的文档化&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.在一个方法声明中出现synchronized修饰符，这个是实现细节，不是导出API的一部分&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.一个类为了可以被多个线程安全地使用，必须在文档中清楚说明它所支持的线程安全级别：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;不可变的&lt;/strong&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;无条件的线程安全&lt;/strong&gt;：可变类，但是有足够的内部同步，可以被并发使用&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;有条件的线程安全&lt;/strong&gt;：某些方法需要外部同步&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;非线程安全&lt;/strong&gt;：必须外部同步&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;线程对立&lt;/strong&gt;：即使所有调用都有外部同步，依旧不能安全地并发使用。通常因为没有同步地修改静态数据&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.必须指明哪个调用序列需要外部同步，还要指明为了调用这个序列，&lt;strong&gt;必须获得哪一把锁&lt;/strong&gt;。如果一个对象代表了另一个对象的一个视图，客户端通常就必须在后台对象上同步，以防止其他线程直接修改后台对象。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.除非从返回类型来看已非常明显，否则静态工厂必须在文档中说明被返回对象的线程安全性&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.私有锁对象必须声明为final的，只能用在无条件线程安全类上，有条件线程安全类不能使用，因为外部调用时无法获得锁。&lt;/p&gt;

&lt;h2 id=&quot;71慎用延迟初始化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;71&lt;/code&gt;.慎用延迟初始化&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.对静态域使用延迟初始化，用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lazy initialization holder class&lt;/code&gt; 模式（静态私有内部类）&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.对实例域使用延迟初始化，用&lt;strong&gt;双重检查模式&lt;/strong&gt;，第二次检查在同步中进行&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果允许重复初始化，用&lt;strong&gt;单重检查模式&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;72不要依赖于线程调度器&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;72&lt;/code&gt;.不要依赖于线程调度器&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.任何依赖于线程调度器来达到正确性或者性能要求的程序，很可能不可移植&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.最好的办法是确保可运行的线程平均数量&lt;strong&gt;不&lt;/strong&gt;明显多于处理器数量&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.如果线程没有在做有意义的工作，就不应该运行&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.不要企图通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thread.yield&lt;/code&gt;来修正程序，它唯一的作用是在测试期间人为增加程序的并发性&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.线程优先级是Java平台上最&lt;strong&gt;不可移植&lt;/strong&gt;的特征&lt;/p&gt;

&lt;h2 id=&quot;73避免使用线程组&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;73&lt;/code&gt;.避免使用线程组&lt;/h2&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十-序列化&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;十&lt;/code&gt; 序列化&lt;/h1&gt;

&lt;h2 id=&quot;74谨慎地实现serializable接口&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;74&lt;/code&gt;.谨慎地实现Serializable接口&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.实现Serializable接口的代价是，&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;一旦一个类被发布，就大大降低了改变这个类实现的可能性&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;增加了出现BUG和安全漏洞的可能性，反序列化是语言之外的对象创建机制&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;随着类发行新的版本，相关的测试负担也增加了&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果没有声明&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serialVersionUID&lt;/code&gt;，系统将在运行时通过类名，接口，成员等自动产生&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.为了继承而设计的类，应该&lt;strong&gt;尽少&lt;/strong&gt;实现Serializable接口，用户接口也应该尽少继承Serializable接口&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果类有一些约束条件，当类的实例域被初始化为默认值，就会违背这些约束条件，此时需要添加&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readObjectNoData&lt;/code&gt;方法，并在其中抛出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvalidObjectException&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.对于为继承而设计的可序列化的类，应该提供一个&lt;strong&gt;无参构造器&lt;/strong&gt;，以及一个&lt;strong&gt;初始化方法&lt;/strong&gt;，readObject与有参构造器共用这个初始化方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AtomicReference&lt;/code&gt;操作原子引用，这是一个很好的线程安全状态机&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.内部类不应该实现Serializable，内部类的默认序列化形式是定义不清楚的，然而静态成员类却可以实现Serializable&lt;/p&gt;

&lt;h2 id=&quot;75考虑使用自定义的序列化形式&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;75&lt;/code&gt;.考虑使用自定义的序列化形式&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.如果没有先认真考虑默认的序列化形式是否合适，则不要贸然接受；理想的序列化形式应该只包含该对象所表示的&lt;strong&gt;逻辑数据&lt;/strong&gt;，而逻辑数据与物理表示法应该是各自独立的&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果一个对象的物理表示法等同于它的逻辑内容，可能就适合于使用默认的序列化形式&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.即使确认了默认的序列化形式是合适的，通常还必须提供一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readObject&lt;/code&gt;方法以保证约束关系和安全性&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.当一个对象的物理表示法与逻辑内容有实质性的区别时，使用默认的序列化形式会有以下4个缺点&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;这个类的导出API永远束缚在该类的内部表示法上&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;消耗过多的空间，因为会反序列化所有的域&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;消耗过多的时间，因为会遍历对象图的拓扑关系&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;引起栈溢出，因为会递归遍历对象图&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defaultWriteObject&lt;/code&gt;方法被调用的时，每个&lt;strong&gt;未&lt;/strong&gt;被标记为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transient&lt;/code&gt;的域都会被序列化；&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defaultReadObject&lt;/code&gt;将会反序列化这些域，在决定将一个域做成非transient之前，一定要确信它的值将是逻辑状态的一部人&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.如果所有的域都是瞬时的，不调用defaultWriteObject和defaultReadObject也是允许的，但是不推荐这么做，可能会引起&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StreamCorruptedException&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;.如果在读取整个对象状态的任何其他方法上强制任何&lt;strong&gt;同步&lt;/strong&gt;，则也必须在对象序列化上强制这种同步&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;.不管选择哪种序列化形式，都要为自己编写的每个可序列化的类声明一个显式的序列版本&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UID&lt;/code&gt;，这样可以避免因为自动生成UID造成不兼容&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt;.如果想为一个类生成一个新的版本，使这个类与现有的类不兼容，那么只需修改UID即可，反序列化时将会发生&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvalidClassException&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;76保护性地编写readobject方法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;76&lt;/code&gt;.保护性地编写readObject方法&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.《&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Java Object Serialization Specification&lt;/code&gt;》&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.当一个对象被反序列化的时候，如果哪个域将被客户端所引用，就必须要做&lt;strong&gt;保护性拷贝&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.不要使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;writeUnshared&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readUnshared&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;writeUnshared：将“未共享”对象写入 ObjectOutputStream。此方法等同于 writeObject，不同点在于它总是将给定对象作为流中惟一的新对象进行写入&lt;/p&gt;

&lt;p&gt;readUnshared：从流中读取一个不共享的对象，同readObject，但不会让后续的readObject和readUnshared调用引用这次调用反序列化得到的对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.readObject不可以调用可被覆盖的方法，一旦调用，这个方法将在子类的域被反序列化之前运行，如果这个方法依赖子类的域，将导致程序失败&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.对于任何约束条件，如果检查失败，则抛出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvalidObjectException&lt;/code&gt;，这写检查应该在所有的保护性拷贝之后&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;.如果整个对象图在反序列化之后必须进行验证，则使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjectInputValidation&lt;/code&gt;接口&lt;/p&gt;

&lt;h2 id=&quot;77对于实例的控制枚举类型优先于readresolve&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;77&lt;/code&gt;.对于实例的控制，枚举类型优先于readResolve&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.在反序列化之后，新建对象上的readResolve方法将被调用，然后该方法返回的对象将取代新建的对象&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.如果依赖readObject进行实例控制，带有对象类型的所有实例域必须声明为transient的&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;.用readResolve进行实例控制并不过时，如果必须编写可序列化的实例受控的类，它的实例在编译时还不知道，你就无法将类表示成枚举&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.如果把readResolve放在一个&lt;strong&gt;final&lt;/strong&gt;类上，它就应该是&lt;strong&gt;私有&lt;/strong&gt;的；如果readResolve方法不是私有的，并且子类没有覆盖它，对序列化过的子类进行反序列化，将会获得一个&lt;strong&gt;超类&lt;/strong&gt;的实例，可能出现ClassCastException&lt;/p&gt;

&lt;h2 id=&quot;78考虑用序列化代理代替序列化实例&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;78&lt;/code&gt;.考虑用序列化代理代替序列化实例&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.实际在流之间传输的是代理对象，反序列化之后，代理对象通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readResolve&lt;/code&gt;返回对象，此时通过构造器创建外围对象，构造器中自带参数检查；序列化时，外围类通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;writeReplace&lt;/code&gt;返回序列化代理实例。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;.writeReplace方法将在对象被写入流之前执行，然后用方法返回的实例替换自身被写入流中，与readResolve相对&lt;/p&gt;
</description>
        <pubDate>Tue, 26 Nov 2013 00:00:00 +0000</pubDate>
        <link>https://bit-ranger.github.io/blog/java/effective-java/</link>
        <guid isPermaLink="true">https://bit-ranger.github.io/blog/java/effective-java/</guid>
        
        <category>Effective</category>
        
        <category>Java</category>
        
        
        <category>Java</category>
        
      </item>
    
  </channel>
</rss>
