<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-20849359</id><updated>2026-02-28T21:51:08.848+08:00</updated><category term="技術"/><category term="隨筆"/><category term="Java"/><category term="個人"/><category term="How To"/><category term="Design Pattern"/><category term="Maven"/><category term="Eclipse"/><category term="心情"/><category term="Perl"/><category term="SVN"/><category term="Tutorial"/><category term="勵志"/><category term="蒙特梭利"/><category term="觀念"/><category term="Apache-commons"/><category term="CVS"/><category term="FixedOrderComparator"/><category term="JSF"/><category term="Json"/><category term="Oracle"/><category term="Spring"/><category term="Struts2"/><category term="comparactor"/><category term="curl"/><category term="jQuery"/><category term="jsoup"/><category term="multi-select"/><category term="sublime"/><category term="tip"/><category term="web service"/><category term="不錯的網站"/><category term="幼兒教育"/><category term="軟體"/><category term="金融"/><title type='text'>太極科技</title><subtitle type='html'>兩儀生四象，五行分陰陽，動靜剛柔，生生不息，是為太極。&lt;br&gt;&#xa;這裡是我的技術日誌，包含：&lt;br&gt;&#xa;- 程式語言、專案管理、系統開發經驗、與一些教學文章&lt;br&gt;&#xa;</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>210</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-20849359.post-646390416471071506</id><published>2024-01-29T09:49:00.002+08:00</published><updated>2024-01-29T09:49:49.021+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="multi-select"/><category scheme="http://www.blogger.com/atom/ns#" term="sublime"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Sublime多重選取</title><content type='html'>&lt;div&gt;&lt;span style=&quot;font-family: Segoe UI, meiryo, Microsoft YaHei, helvetica, arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;「多重選取」是指在使用文字編輯器時，一種特殊的選取行為。不同一一般的選取動作，這種選取的區域並不連續。你可以同時整篇選取文章中相同的字，或是一個矩形的區塊。我大多數會在「重構」程時使用。記一些熱鍵對使用上來說會有很大的便利。以下是在sublime在多重選取操作上的一些熱鍵：&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul style=&quot;border: 0px; font-family: &amp;quot;Segoe UI&amp;quot;, meiryo, &amp;quot;Microsoft YaHei&amp;quot;, helvetica, arial, sans-serif; font-size: 14px; list-style-image: initial; list-style-position: initial; margin: 0px 0px 1em; outline: 0px; padding: 0px 0px 0px 2em; vertical-align: baseline;&quot;&gt;&lt;li data-line=&quot;6&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;6&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;垂直選取(column select):&lt;br /&gt;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+alt+↑/↓&lt;/code&gt;&lt;br /&gt;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;shift+右鍵&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;10&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;10&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;增加選取區:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+shift+右鍵&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;11&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;11&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;減少選取區:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;alt+shift+右鍵&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;13&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;13&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;回上一步:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+u&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;14&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;14&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;跳過下一步:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+k&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;15&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;15&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;選擇下一個:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+d&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;16&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;16&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;搜尋+&lt;strong style=&quot;border: 0px; font-family: inherit; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;選擇&lt;/strong&gt;全部:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;alt+F3&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li data-line=&quot;17&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;p data-line=&quot;17&quot; style=&quot;border: 0px; font-family: inherit; font-style: inherit; font-weight: inherit; line-height: 1.6em; margin: 0px; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;跳到下一個&lt;strong style=&quot;border: 0px; font-family: inherit; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;搜尋&lt;/strong&gt;:&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+F3&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-line=&quot;19&quot; style=&quot;border: 0px; font-family: &amp;quot;Segoe UI&amp;quot;, meiryo, &amp;quot;Microsoft YaHei&amp;quot;, helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6em; margin: 0px 0px 1em; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;如果選過頭可以垂直選取後，再執行上一步。&lt;br /&gt;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+alt+↑/↓&lt;/code&gt;&amp;nbsp;後+&amp;nbsp;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+u&lt;/code&gt;&lt;br /&gt;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+d&lt;/code&gt;後+&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+u&lt;/code&gt;&lt;/p&gt;&lt;p data-line=&quot;23&quot; style=&quot;border: 0px; font-family: &amp;quot;Segoe UI&amp;quot;, meiryo, &amp;quot;Microsoft YaHei&amp;quot;, helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6em; margin: 0px 0px 1em; outline: 0px; overflow-wrap: break-word; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;alt+shift+←/→&lt;/code&gt;可以選字(subwords)。&lt;code style=&quot;background-color: #f4f4f4; border-radius: 5px; border: 1px solid rgb(217, 217, 217); color: inherit; direction: ltr; font-family: Consolas, Monaco, Menlo, &amp;quot;Ubuntu Mono&amp;quot;, Consolas, source-code-pro, monospace; font-size: 1em; font-style: inherit; font-weight: inherit; margin: 0px 2px 0px 0px; outline: 0px; padding: 2px; vertical-align: baseline;&quot;&gt;ctrl+shift+←/→&lt;/code&gt;也可以一次選一段，但切段點較不固定。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/646390416471071506/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/646390416471071506' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/646390416471071506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/646390416471071506'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2024/01/sublime.html' title='Sublime多重選取'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-4427582552672991654</id><published>2023-05-31T17:03:00.001+08:00</published><updated>2023-05-31T17:03:40.969+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="curl"/><category scheme="http://www.blogger.com/atom/ns#" term="web service"/><title type='text'>如何使用CURL來進行web service測試</title><content type='html'>&lt;h3 style=&quot;text-align: left;&quot;&gt;輸入參數&amp;nbsp;&lt;/h3&gt;&lt;p&gt;curl是linux上一個測試連線的工具。如果輸入參數很簡單，我們可以直接下參數指令進行測試：&lt;/p&gt;&lt;code&gt;$ curl -X POST -H &quot;Content-Type: application/json&quot; -d &#39;{&quot;status&quot; : false, &quot;name&quot; : &quot;Jack&quot;}&#39; &quot;http://www.example.com/api/resources&quot;&lt;/code&gt;&lt;p&gt;當輸入參數是XML或json格式時，可以將輸入內容存放在一個檔案中，再進行測試。&lt;/p&gt;&lt;code&gt;curl --header &quot;content-type: text/soap+xml; charset=utf-8&quot;--data @sample.xml http://$HOSTNAME:$PORT/$SOMEPATH&lt;/code&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;sample.xml&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;&amp;lt;?xml version=&#39;1.0&#39; encoding=&#39;utf-8&#39;?&amp;gt;&lt;br /&gt;&amp;lt;soapenv:Envelope xmlns:soapenv=&quot;http://www.w3.org/2003/05/soap-envelope&quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;soapenv:Body&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;ns0:some_operation xmlns:ns0=&quot;http://some_ns_uri&quot;/&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/soapenv:Body&amp;gt;&lt;br /&gt;&amp;lt;/soapenv:Envelope&amp;gt;&lt;/code&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;輸入帳密&lt;/h3&gt;&lt;p&gt;若所存取網頁有使用 Basic Authentication認證，可以使用 `--user username:password` 來通過驗證：&lt;/p&gt;&lt;code&gt;$ curl -i --user secret:vary_secret http://www.example.com/api/resources&lt;/code&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/4427582552672991654/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/4427582552672991654' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4427582552672991654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4427582552672991654'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2023/05/curlweb-service.html' title='如何使用CURL來進行web service測試'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-4319141183504934178</id><published>2019-05-13T11:12:00.000+08:00</published><updated>2019-11-11T09:46:29.836+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache-commons"/><category scheme="http://www.blogger.com/atom/ns#" term="comparactor"/><category scheme="http://www.blogger.com/atom/ns#" term="FixedOrderComparator"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><title type='text'>FixedOrderComparator簡介</title><content type='html'>&lt;p&gt;Apache commons collections 是一套專門針對&lt;code&gt;Collection&lt;/code&gt;進行處理的一系列工具。其中提供了一些&lt;code&gt;Comparactor&lt;/code&gt;，可以針對&lt;code&gt;Collection&lt;/code&gt;來進行處理。這些&lt;code&gt;Comparactor&lt;/code&gt;，大都放在&lt;code&gt;org.apache.commons.collections.comparators&lt;/code&gt; package中。本篇文章主要針對其中的&lt;code&gt;FixedOrderComparator&lt;/code&gt;進行demo: &lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;以下是範例:  &lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code &gt;String[] data = new String[] { &amp;quot;GUEST&amp;quot;, &amp;quot;NORMAL&amp;quot;, &amp;quot;NORMAL&amp;quot;, &amp;quot;PAID&amp;quot;, &amp;quot;VIP&amp;quot;, &amp;quot;VIP&amp;quot;, &amp;quot;GUEST&amp;quot;, &amp;quot;PAID&amp;quot; };
System.out.println(Arrays.toString(data));
Comparator&amp;lt;String&amp;gt; comparator = new FixedOrderComparator(new String[] { &amp;quot;PAID&amp;quot;, &amp;quot;VIP&amp;quot;, &amp;quot;NORMAL&amp;quot;, &amp;quot;GUEST&amp;quot; });
Arrays.sort(data, comparator);
System.out.println(Arrays.toString(data));

[GUEST, NORMAL, NORMAL, PAID, VIP, VIP, GUEST, PAID]
[PAID, PAID, VIP, VIP, NORMAL, NORMAL, GUEST, GUEST]
&lt;/code&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/4319141183504934178/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/4319141183504934178' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4319141183504934178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4319141183504934178'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2019/05/FixedOrderComparactorTutorial.html' title='FixedOrderComparator簡介'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-378653228615238839</id><published>2019-05-10T21:54:00.000+08:00</published><updated>2019-05-13T10:51:39.730+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="How To"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>如何在透過class指定相對路徑</title><content type='html'>&lt;p&gt;取得一個檔案有很多方法。一種是你知道絕對路徑。這是最簡單而且最穩定的方法。但很多時候，事情沒那麼簡單。因為你可能不知道系統在實際上線的時候，他機器的配置是怎樣。很多時候，你只知道他從工作目錄下的相對路徑時，這時該如何去指定這個檔案的路徑呢？&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;舉例來說，要讀取一個檔案，在開發的時候，可以知道這個檔案在我電的的本機的D槽，我可以輕而易舉的讀到他；可是在上線的時候，卻不知道這個檔案在那個槽(甚至搞不好上線的機器，根本就沒有&amp;quot;槽&amp;quot;，對，我說的就是linux)，只知道他的路徑是在：&lt;code&gt;/db/crm.db&lt;/code&gt;下，這時候該怎麼辦呢？&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/40851621153_f2fd1668c4_m.jpg&quot; /&gt;&lt;/p&gt;&lt;h1&gt;解決方式&lt;/h1&gt;&lt;p&gt;&lt;code&gt;List&amp;lt;String&amp;gt; lists = IOUtils.readLines(this.getClass().getResourceAsStream(&amp;quot;/db/crm.db&amp;quot;), &amp;quot;utf-8&amp;quot;);&lt;/code&gt;&lt;/p&gt;&lt;h1&gt;重點&lt;/h1&gt;&lt;ol&gt;&lt;li&gt;主要是使用&lt;code&gt;getClass().getResourceAsStream()&lt;/code&gt;。取得&lt;code&gt;InputStream&lt;/code&gt;後需要做什麼不在討論範圍內。&lt;/li&gt;
&lt;li&gt;需要使用 &amp;quot;/&amp;quot;開始。如果沒有&amp;quot;/&amp;quot;，則從目前的package開始找起。如果getClass()是&lt;code&gt;com.foo&lt;/code&gt;那就是從&lt;code&gt;src/com/foo&lt;/code&gt;開始找起；用另一種講法，就是跟目前class相同目錄。&lt;/li&gt;
&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/378653228615238839/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/378653228615238839' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/378653228615238839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/378653228615238839'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2019/05/class.html' title='如何在透過class指定相對路徑'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-6025592864942842347</id><published>2019-03-24T22:32:00.000+08:00</published><updated>2019-03-24T22:32:27.251+08:00</updated><title type='text'>如何使用 Struts 1.3如何接收json資料</title><content type='html'>&lt;p&gt;Struts是一個很舊的framework。不過在一些系統中，總會看到一些殘留的痕跡，硬是還在用這種framework。而新的技術仍在發展。一個例子就是web service。而當web service遇到Struts 1.3時，就很尷尬了。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;目前的solution就是…&lt;strong&gt;自己parse&lt;/strong&gt;。在這樣的solution下，基本上，不論那一個framework都可以使用這樣的解法。好吧，這完全跟struts沒有任何關係。這完全是標題殺手。換另一種標題就是：&lt;strong&gt;如何使用servlet解析json資料&lt;/strong&gt;。不過以下要展示的重點，也不在json解析上，而是在如何處理&lt;code&gt;httpRequest&lt;/code&gt;傳來的&lt;strong&gt;資料&lt;/strong&gt;。注意，是&lt;strong&gt;資料&lt;/strong&gt;，不是參數。也就是說，當web service即使以xml來傳遞資訊時，稍稍變化也依然可以應用。正確的標題應該是：&lt;strong&gt;如何使用servlet接收message body資料&lt;/strong&gt;。至於什麼是message body，請參閱&lt;a href=&quot;https://tools.ietf.org/html/rfc7230#section-3.3&quot;&gt;HTTP規格書&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;以下是解析message body(外加轉為json)的範例程式碼：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;public ActionForward applyResult(ActionMapping mapping,
 ActionForm form, HttpServletRequest req, HttpServletResponse res) {
        try {
            StringBuffer sb = new StringBuffer();
            String line = null;
            BufferedReader reader = req.getReader();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            String sbStr = sb.toString();
            logger.debug(&amp;quot;JSONStr:&amp;quot; + sbStr);
            JSONObject jsonObject = JSONObject.fromObject(sbStr);
            YOURBEAN resp = (YOURBEAN) JSONObject.toBean(jsonObject, YOURBEAN.class);
            logger.debug(&amp;quot;JsonBean:&amp;quot;+resp);
            return mapping.findForward(&amp;quot;result&amp;quot;);
        } catch (Exception ex) {
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;參考: &lt;a href=&quot;https://stackoverflow.com/questions/3831680/httpservletrequest-get-json-post-data&quot;&gt;java - HttpServletRequest get JSON POST data - Stack Overflow&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/6025592864942842347/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/6025592864942842347' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/6025592864942842347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/6025592864942842347'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2019/03/how-to-parse-json-data-by-struts1.html' title='如何使用 Struts 1.3如何接收json資料'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-8217727456666385655</id><published>2018-12-28T00:22:00.000+08:00</published><updated>2018-12-29T03:54:32.237+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="How To"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Java中如何截取固定長的字串</title><content type='html'>&lt;br /&gt;
&lt;p&gt;Java中截取字串的方法有很多，一般的情況下，我習慣使用Apache的&lt;code&gt;StringUtils&lt;/code&gt;來處理。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;這是用來將字串進行縮寫的。不過StringUtils的字串截取，是根據char來截取。中文字算一個char、英文字算一個char。長度500的字串，就是500個字。範例如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;StringUtils.abbreviate(str, maxWidth);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在某些資料庫中, 例如Oracle，可以開立欄位長度為單位為byte的欄位(&lt;code&gt;varchar2(n byte)&lt;/code&gt;)，這種欄位形不分中英文長度。當需要將適當長度的字串存入這樣的資料庫欄位時，截取長度的基準，就需要使用byte來計算。而中文字一般佔3byte，英文字是1 byte。有另外一種說法是中文字佔2byte，不過不管長度是多少，反正歐亞字元的編碼長度，就跟密碼一樣，我都搞不清。不過這並不影響我面接下來的內容。所以長度500的字串，在中英文夾雜的情況下，肯定不到500個字。解決的方法，主要是用&lt;code&gt;Arrays.copyOf()&lt;/code&gt;這個method。&lt;/p&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;    private String abbreviate(String s) {
        byte[] sb = s.getBytes();
        final int max = 480;
        byte[] newsb = Arrays.copyOf(sb, max);
        return new String(newsb).trim();
    }
&lt;/code&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/8217727456666385655/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/8217727456666385655' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/8217727456666385655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/8217727456666385655'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2018/12/howto-substring-by-fix-length.html' title='Java中如何截取固定長的字串'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-9193177618286011801</id><published>2018-12-19T11:16:00.000+08:00</published><updated>2018-12-19T11:19:54.809+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Maven"/><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial"/><title type='text'>Maven從0開始</title><content type='html'>&lt;h1&gt;我眼中的maven&lt;/h1&gt;&lt;p&gt;Marvn是apache出的一個工具。他主要用來提供library的管理。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;他可以讓你在你的專案裡，快速的新增刪改library。這個特色，跟你手動新增、刪除library的差異在於，可以快速的處理library之間的dependence。 所謂dependence就是我開發的程式中，需要用到A library，而這個A library會用到另一個B library V1版的程式，程式中用到的另一個C library會用到B library V2版的程式，這樣的一套接一套、一環接一環的關係。這時候系統架構師或是系統設計師，就必需先統計需要用到那些library，然後到各個官網去下載各種版本的library。 有時候A library會用到B、C、D，C會用到D、E、F，這當中的情恨糾葛、悱惻纏綿是搞不清、理還亂。而maven可以讓你透過簡單的設定，讓你在架構的project、系統時，維護這些關係亂七八糟的library。&lt;/p&gt;&lt;h1&gt;Apache 官網眼中的maven&lt;/h1&gt;&lt;p&gt;當然maven提供的功能不只這樣，他可以直接包WAR檔、在deploy前進行測試、還有很多，我都搞不清楚的功能。某種程度上，他可以跟Apache早期出的另一套工具叫ant的進行比較。所謂的可以進行比較，意思是說他有很多相同，也有很多不同。基本上，他們的架構、理念，完全不一樣，但是功能上，有些重複。例如ant沒有提供library的管理、但ant與maven都有提供編譯、包WAR檔的功能。扯遠了，讓我們回到maven。&lt;/p&gt;&lt;h1&gt;深入探討&lt;/h1&gt;&lt;p&gt;讓一個從未接觸過maven的人，認知道maven是什麼，這樣的探討，大概就到這，不能再更多了。再多下去，就不是一個未接觸maven的人所可以接受的了。&lt;br /&gt;
如果你覺得你已經不再是一個「從未接觸過maven的人」接下來有關於maven的學習，就是maven的架構、設計理念、理論基礎、及如何使用了。這種東西，google是你最好的朋友，當然你也可以參考&lt;a href=&quot;https://taichitech.blogspot.com/2013/09/maven.html&quot;&gt;Maven 101&lt;/a&gt;，&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/9193177618286011801/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/9193177618286011801' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/9193177618286011801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/9193177618286011801'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2018/12/maven-start-from-0.html' title='Maven從0開始'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-1957345020441681564</id><published>2015-05-27T13:08:00.000+08:00</published><updated>2015-06-01T10:30:46.397+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="jQuery"/><category scheme="http://www.blogger.com/atom/ns#" term="Json"/><category scheme="http://www.blogger.com/atom/ns#" term="Struts2"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>如何使用jQuery達成動態查詢資料─以jQuery傳送Json給Struts2</title><content type='html'>&lt;h1 id=&quot;-&quot;&gt;前言&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;http://c4.staticflickr.com/8/7701/18044913442_8b066fd6b2_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;我想大家都有需要在web系統上，使用動態查詢的功能。 只要在查詢欄位上輸入查詢關鍵字，系統就直接顯示出查詢結果。企業需求在於：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;不要再做任何的點擊動作，&lt;/li&gt;
&lt;li&gt;整個頁面不用重新整理。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;聽說最近jQuery很紅；也正好搭配新學習的Struts2，示範一下如何應用Struts2。 僅管這標題下得有點像是論文題目，不過內容肯定沒那麼嚴謹。 所以大家可以放輕鬆看一下。 這篇主要的目的，雖然在講解如何達成動態資料查詢的這項企業需求，不過也會再稍微講解一下裡面的東西。 一樣的；也都是分2階段：簡單的，和詳盡的─知其然，與知其所以然。 &lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;架構&lt;/h1&gt;&lt;p&gt;基本上，需求很簡單，就是在頁面上的一個&lt;code&gt;input&lt;/code&gt;欄位，輸入查詢關鍵字，畫面即時呈現所查詢的資料結果。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;知其然&lt;/h1&gt;&lt;p&gt;基本上是使用struts2、jquery 2個框架；不過struts2有再額外新增json-plgin&lt;/p&gt;&lt;h2 id=&quot;pom-xml&quot;&gt;pom.xml&lt;/h2&gt;&lt;p&gt;搭配Maven&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;taglibs&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;standard&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.struts&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;struts2-json-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${struts2.version}&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;strts-xml&quot;&gt;strts.xml&lt;/h2&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;&amp;lt;package name=&amp;quot;taichitech&amp;quot; extends=&amp;quot;struts-default, json-default&amp;quot; namespace=&amp;quot;/&amp;quot;&amp;gt;
    &amp;lt;action name=&amp;quot;searchData&amp;quot; class=&amp;quot;taichitech.jblog.struts2.SearchData&amp;quot; method=&amp;quot;searchData&amp;quot;&amp;gt;
        &amp;lt;result type=&amp;quot;json&amp;quot; name=&amp;quot;success&amp;quot;&amp;gt;
        &amp;lt;/result&amp;gt;
    &amp;lt;/action&amp;gt;
&amp;lt;/package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其中一個重點在於需要繼自json-default package。&lt;/p&gt;&lt;h1 id=&quot;java-action&quot;&gt;Java action&lt;/h1&gt;&lt;p&gt;Struts2 action的部份&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;private String condition;

private Map&amp;lt;String, Object&amp;gt; returnData;

public String searchData() {
    logger.debug(&amp;quot;Search condition: {}:&amp;quot;, condition);
    returnData = new HashMap&amp;lt;String, Object&amp;gt;();

    char[] conditions = condition.toCharArray();
    StringBuffer reg = new StringBuffer();
    for (char c : conditions) {
        reg.append(&amp;quot;.*&amp;quot;);
        reg.append(c);
    }
    reg.append(&amp;quot;.*&amp;quot;);
    String regex = reg.toString();
    ArrayList&amp;lt;DataVo&amp;gt; vos = new ArrayList&amp;lt;DataVo&amp;gt;();
    for (DataVo dataVo : DATA_SET) {
        if (dataVo.getName().matches(regex) || dataVo.getUrl().matches(regex)) {
            vos.add(dataVo);
        }
    }
    returnData.put(&amp;quot;SEARCH_RESULT&amp;quot;, vos);
    returnData.put(&amp;quot;COUNT&amp;quot;, vos.size());
    return SUCCESS;
}
// Getters and setters of returnData、 condition.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上段程式碼，有用了一個vo；Vo有2個property、&lt;code&gt;name&lt;/code&gt;與&lt;code&gt;url&lt;/code&gt;。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;public class DataVo {
    DataVo(String name, String url) {
        this.name = name;
        this.url = url;
    }
    private String name;
    private String url;
//Getters and setters
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;jsp-script&quot;&gt;JSP script&lt;/h2&gt;&lt;p&gt;JSP script 部份&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;&amp;lt;script language=&amp;quot;javascript&amp;quot;&amp;gt;
    var showdata = function() {
        $.ajax({
            url : &amp;quot;${pageContext.request.contextPath}/searchData.action&amp;quot;,
            type : &amp;quot;POST&amp;quot;,
            async : &amp;quot;false&amp;quot;,
            data : {
                &amp;#39;condition&amp;#39; : $(&amp;quot;#condition&amp;quot;).val()
            },
            dataType : &amp;quot;json&amp;quot;,
            success : function(jsonData) {
                var html = &amp;#39;&amp;#39;;
                $.each(jsonData.returnData.SEARCH_RESULT, function(index, val){
                    html=html+val.url+&amp;#39;&amp;lt;br/&amp;gt;&amp;#39;;
                });
                html+=&amp;quot;Total: &amp;quot;+ jsonData.returnData.COUNT;
                $(&amp;quot;#searchResult&amp;quot;).html(html);
            }
        });
    };
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;jsp-html&quot;&gt;JSP Html&lt;/h2&gt;&lt;p&gt;JSP Html的部份，單純到不行…&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
    Search: &amp;lt;input id=&amp;quot;condition&amp;quot; type=&amp;quot;text&amp;quot; onkeyup=&amp;quot;showdata(this);&amp;quot; /&amp;gt;
    &amp;lt;div id=&amp;quot;searchResult&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-&quot;&gt;結果&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;http://c2.staticflickr.com/6/5463/17860314698_22f215b273_o.png&quot; alt=&quot;&quot;&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;基本上，我們的這套demo是使用struts2與jquery，上面沒有展示到的部份是，如何引用jQuery。我將程式碼補列列在下面。&lt;/p&gt;&lt;p&gt;在jsp中引用jQuery；jQuery的檔案放在/js目錄下。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;${pageContext.request.contextPath}/js/jquery-1.10.2.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;${pageContext.request.contextPath}/js/jquery-ui-1.10.4.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;-pom-xml&quot;&gt;有關於pom.xml&lt;/h2&gt;&lt;p&gt;這個展示使用了maven的方式。使用maven可以讓我只要copy paste簡單的幾個dependence，就可以自動匯入jar檔了！而且好處還在於可以省略許多不用的jar檔。 在沒有maven的時候，從各官網下載下來的package，總包山包海，但我其實並沒有需要使用到那個framework所有的模組，可是在一開始學習的時候，時常搞不清楚那些jar檔該放、那些jar檔不該放。 使用maven會幫你將這個問題處理掉。&lt;/p&gt;&lt;p&gt;在pom的部份，我並沒有撰寫struts-core的部份jar檔，但是我有指定說我要使用struts2-json。&lt;strong&gt;Maven 會自動的將所需要使用到的模組&lt;/strong&gt;由repository下載下來。&lt;/p&gt;&lt;p&gt;你可以參考：&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://taichitech.blogspot.tw/2013/09/maven.html&quot;&gt;Maven 101：淺談Maven&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://maven.apache.org&quot;&gt;Maven 官網&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;-struts-xml&quot;&gt;有關於struts.xml&lt;/h2&gt;&lt;p&gt;這是很簡單的struts(struts2) 的宣告。全部的程式碼，與之前所列的沒有太大的差異，就請各位讀在在實作的時候，請自行補足缺少的部份。 唯一個需要注意的重點是你的action必需要繼承&lt;code&gt;json-default&lt;/code&gt;這個package。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://c4.staticflickr.com/8/7670/18022561516_816260de50_o.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;圖片引用自網路。缺少的部份程式碼，請各位自行補充了。&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;-java-action&quot;&gt;有關於Java action&lt;/h2&gt;&lt;p&gt;Action的部份，有2個重點。 &lt;/p&gt;&lt;p&gt;其中一個是屬性的部份。 &lt;/p&gt;&lt;p&gt;網頁的輸入會被存入&lt;code&gt;condition&lt;/code&gt;這個property，回傳的資料以&lt;code&gt;Map&lt;/code&gt;的型態儲存。 主要查詢結果資料集，key值是&lt;code&gt;SEARCH_RESULT&lt;/code&gt;；另外還有一個key為&lt;code&gt;COUNT&lt;/code&gt;的資料，代表著查詢出來的結果筆數。 如何取得這2個資訊，當然是依系統而有不同的實作，case by case。 請注意，這裡的根結點，是action部份。 你可以在struts.xml加上&lt;code&gt;&amp;lt;param name=&amp;quot;root&amp;quot;&amp;gt;&lt;/code&gt;，額外指定根結點。&lt;/p&gt;&lt;p&gt;struts.xml&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;&amp;lt;action ... &amp;gt;
    &amp;lt;result type=&amp;quot;json&amp;quot; name=&amp;quot;success&amp;quot;&amp;gt;
        &amp;lt;param name=&amp;quot;root&amp;quot;&amp;gt;returnData&amp;lt;/param&amp;gt;
    &amp;lt;/result&amp;gt;
&amp;lt;/action&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;http://c4.staticflickr.com/8/7786/17860314818_7ab1f9891c_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;直接呼叫Struts2Action，檢視回傳結果。 Struts2回傳的資料只是單純的文字格式&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;&lt;img src=&quot;https://c4.staticflickr.com/8/7793/17951861258_0f1e1f4efd_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt; 加上了&lt;code&gt;&amp;lt;param name=&amp;quot;root&amp;quot;&amp;gt;&lt;/code&gt; 回傳值與之前的不一樣。根結點由&lt;code&gt;returnData&lt;/code&gt;開始&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;第2個重點是&lt;code&gt;searchData()&lt;/code&gt; 這個method。 &lt;/p&gt;&lt;p&gt;&lt;code&gt;searchData()&lt;/code&gt;是主要的搜尋程式邏輯所在。 他依據使用者所輸入字元 ，對資料集進行搜尋，使用者每次在查詢輸入欄位每敲一個字，就會對server做一次request，可想而之，其實&lt;strong&gt;這種企業功能是很秏資源的&lt;/strong&gt;。 當然這部份是可以再加強的部份，不過這已經超過本篇範籌了。&lt;/p&gt;&lt;p&gt;搜尋的部份，使用了正規表示的功能。&lt;/p&gt;&lt;p&gt;這裡分解使用者的輸入，使用了迴圈，產生表示式，並對資料加以比對。比對的部份，包含了&lt;code&gt;DataVo&lt;/code&gt;的&lt;code&gt;name&lt;/code&gt;與&lt;code&gt;url&lt;/code&gt;2個屬性。 在這樣的程式下，你可以輸入&lt;code&gt;ggc&lt;/code&gt;查詢到&lt;strong&gt;g&lt;/strong&gt;oo&lt;strong&gt;g&lt;/strong&gt;ole.&lt;strong&gt;c&lt;/strong&gt;om，只要name或url符合都會被篩選出來。當然這部份也是case by case。&lt;/p&gt;&lt;h2 id=&quot;jsp-script-&quot;&gt;JSP script 的部份&lt;/h2&gt;&lt;p&gt;JavaScript的部份，使用了jQuery。 Ajax 的部份，是使用了ajax()這支method。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;jQuery.ajax( url [, settings ] )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;延伸參考 jQuery 官方 API: &lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://api.jquery.com/jquery.ajax&quot;&gt;jQuery ajax() API&lt;/a&gt;&lt;/p&gt;&lt;p&gt;特別強調以下幾點：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;data&lt;/code&gt;：需要傳回server的參數。必需為VO、String、或是Array。 VO是&lt;code&gt;key-value&lt;/code&gt;的型態。 預設會以GET的方式加在URL後。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dataType&lt;/code&gt;：預計從server要回傳的資料型態。你可以有xml, json, script, or html這些選項。如果不指定的話，jQuery會自己猜…，這種不確定的東西，還是指定比較恰當。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt;：也可以用&lt;code&gt;method&lt;/code&gt;。 表jQuery提交資料的方式。 預設是&lt;code&gt;GET&lt;/code&gt;。 以資安來說，太危險了，請一定要用&lt;code&gt;POST&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;async&lt;/code&gt;：說明request以同步或非同步的方式傳送給server。預設是非同步。 非同步的好處，雖然看似可以加快反應速度，但是也同時表示，在資料還沒有完全傳送完畢前，後端就可以開始動作；如果開始處理還沒傳送過來的資料有可能就會出事。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;success&lt;/code&gt;：像&lt;code&gt;success: function(){};&lt;/code&gt;裡的function 稱為callback function。表示jQuery在表示success後會繼處理function裡面的部分。&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;jsonData&lt;/code&gt;：雖然以struts2 的Action回傳的結果，看似是json型態，可是在&lt;strong&gt;jQuery 1.5以後&lt;/strong&gt;，jsonData卻被封裝成名為&lt;code&gt;jqXHR&lt;/code&gt;(jQuery自己的XmlHttpRequest)的物件。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://c1.staticflickr.com/9/8798/18138120371_88bf4a9f28_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;jQuery並不認回資料只是單純的文字檔&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;範例上直接使用&lt;code&gt;jsonData.returnData.SEARCH_RESULT&lt;/code&gt;取得集合，將資料傳給接下來的jQuery的&lt;code&gt;each()&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;each()&lt;/code&gt;有2種，參數是array或是object。如果是object ，則必需是&lt;em&gt;key-value&lt;/em&gt;的格式。。詳細格式是這樣：&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;jQuery.each( array, callback )
callback
Type: Function( Integer indexInArray, Object value )
&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;jQuery.each( object, callback )
callback
Type: Function( String propertyName, Object valueOfProperty )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你還可以參考： &lt;a href=&quot;https://api.jquery.com/jQuery.each&quot;&gt;jQuery Each API doc&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;結語&lt;/h1&gt;&lt;p&gt;這裡展現了一個完整的實作，可以進行動態查詢。 所使用的技術，包含jQuery、Json、Struts2、Maven。 文中你可以看到struts2使用&lt;code&gt;result type=&amp;#39;jason&amp;#39;&lt;/code&gt;的回傳結果。 文中也稍微介紹了jQuery &lt;code&gt;ajax()&lt;/code&gt;與&lt;code&gt;each()&lt;/code&gt;2支api的用法。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/1957345020441681564/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/1957345020441681564' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/1957345020441681564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/1957345020441681564'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/05/HowToConnStruts2ByJQueryAndJson.html' title='如何使用jQuery達成動態查詢資料─以jQuery傳送Json給Struts2'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-6192028703147846037</id><published>2015-05-19T10:30:00.000+08:00</published><updated>2015-05-22T10:32:15.789+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>你的第一隻Java程式─Hello World</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://c1.staticflickr.com/9/8855/17653880448_0df50d38cd_o.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;圖片引用自&lt;a href=&quot;http://bestmag.net&quot;&gt;bestmag.net&lt;/a&gt;&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;一開始學習語言，我的慣例都會先給hello world!! 可惜在Hello world之前，還是有一些事必需要做，也有一些東西必需要先了解…&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;安裝&lt;/h1&gt;&lt;h2 id=&quot;java-&quot;&gt;Java 下載&lt;/h2&gt;&lt;p&gt;先別急著想打開文字編輯器，copy 一份程式碼，然後paste上去。 理由除了你必需要先了解你在copy 些啥鬼之外，你還必需先安裝Java，不然你paste完後，你會不知道如何去執行他。 不過題外話，在初學階段，我還是&lt;strong&gt;強烈&lt;/strong&gt;建議你純手動敲一次程式碼。&lt;/p&gt;&lt;p&gt;你必需先到Java 官網去下載Jdk。 目前(2015/5) Java的維護者是Oracle；基本上你在網路上打了關鍵字去搜尋一下，應該就會對了。我先列出連結，但很可能在未來不久的日子裡，這個連結會失效。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/index.html&quot;&gt;Oracle java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;下載版本有幾點需要注意：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;符合你的作業系統&lt;/li&gt;
&lt;li&gt;Java SE。 注意，不是Java EE。&lt;/li&gt;
&lt;li&gt;JDK。注意，不是JRE。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;如果你不清楚Java SE與Java EE的差別，或是JDK與JRE的差別，你可以參考&lt;a href=&quot;http://taichitech.blogspot.tw/2015/05/java-overview.html&quot;&gt;Java 101-Overview&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;下載下來之後，就是無腦安裝的「下一步」、「下一步」…。&lt;em&gt;(Windows 限定)&lt;/em&gt; &lt;/p&gt;&lt;p&gt;&lt;em&gt;僅管是無腦安裝，我還是很建議你，仔細看看你安裝了些啥鬼。或許你現在還不知道你道底安裝了些啥，但在往後的日子裡，你將會學到這些東西。&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;環境設定&lt;/h2&gt;&lt;p&gt;安裝完後，你必需再設定3個環境變數。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;JAVA_HOME&lt;/code&gt;。 指到你剛安裝的Jave的目錄。&lt;br /&gt;
&lt;img src=&quot;http://c1.staticflickr.com/9/8756/17653280840_981768ae3b_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;CALSSPATH&lt;/code&gt;。 用「;」設定2個值：1個是你的工作目錄─「.」；1個是&lt;code&gt;JAVA_HOME\lib&lt;/code&gt;裡的&lt;code&gt;tools.jar&lt;/code&gt;。&lt;br /&gt;
&lt;img src=&quot;http://c1.staticflickr.com/9/8871/17654560209_1130f030e2_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PATH&lt;/code&gt;；指向&lt;code&gt;JAVA_HOME&lt;/code&gt;裡的bin目錄。&lt;br /&gt;
&lt;img src=&quot;http://c2.staticflickr.com/8/7788/17840813135_c7a10c62b6_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;h2 id=&quot;-&quot;&gt;環境設定：這是尛？&lt;/h2&gt;&lt;p&gt;&lt;code&gt;JAVA_HOME&lt;/code&gt;，是說明你目前的工作環境下，你正在執行那一個Java。 這表示，你可以在同一一台機器下，安裝多個JDK與JRE。&lt;/p&gt;&lt;p&gt;&lt;code&gt;CLASSPATH&lt;/code&gt;；是告訴Java─其實是告訴JVM─你可以引用的其他的模組或函式庫。 因為我還沒有介紹什麼是library，所以就先用「模組」、「函式庫」這樣的翻譯名詞…。好吧，&lt;code&gt;library&lt;/code&gt;就是「函式庫」。 如果你還對什麼是「函式庫」或是「模組」還很模糊，沒關係，就先暫時打住，在往後的日子裡，你終究會體會到。現在還只是hello world... 。 &lt;/p&gt;&lt;p&gt;&lt;code&gt;tools.jar&lt;/code&gt;這個函式，就是在你編譯程式時，會用到的一個函式庫。原則上Java的函式庫，副檔名都是用&lt;code&gt;.jar&lt;/code&gt;。 &lt;em&gt;其實這只是一個簡單的壓縮檔&lt;/em&gt;，沒什麼特殊的編碼內容。你可以使用WinRAR或是WinZip，或是任何一套可以解壓縮的程式，來檢視其內容。&lt;/p&gt;&lt;p&gt;&lt;code&gt;PATH&lt;/code&gt;；這是告訴作業系統你的程式要到那些路徑下去搜尋&lt;code&gt;java.exe&lt;/code&gt;這個執行檔。&lt;/p&gt;&lt;h1 id=&quot;-hello-world-&quot;&gt;可以Hello World了嗎？&lt;/h1&gt;&lt;p&gt;還不行。 到目前為止，你大致完成一個HelloWorld的前置準備。接下來，你還差一個文字編輯器。&lt;/p&gt;&lt;p&gt;你可以使用任意一款文字編輯器，但是要注意一點，就是儲存格式需要可以儲存成&lt;strong&gt;UTF-8 without BOM &lt;/strong&gt;或是編碼格式。 &lt;/p&gt;&lt;p&gt;BOM是…一種東西；雖然會越帶越多，不過為了滿足一部分讀者的好奇心，還是要簡單帶過一下，更多的就需要再另外開篇幅了。 BOM是&lt;strong&gt;B&lt;/strong&gt;yte-&lt;strong&gt;O&lt;/strong&gt;rder &lt;strong&gt;M&lt;/strong&gt;ark，是編輯軟體在存檔時，在檔案前面加的一個註記，說明這檔案的詳細格式。 UTF-8 with BOM是微軟專用的格式。 Java 一般開發&lt;strong&gt;不&lt;/strong&gt;採用這種；所以一般使用without BOM。&lt;/p&gt;&lt;h1 id=&quot;-hello-world-&quot;&gt;我要Hello World~~&lt;/h1&gt;&lt;h2 id=&quot;-hello-world&quot;&gt;開始Hello World&lt;/h2&gt;&lt;p&gt;好吧!! 我們真的可以開始撰寫一個Hello World了。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;請找一個乾淨的目錄。 這個目錄會是你的工作目錄。 我的demo會是這樣：&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;d:\Temps\JavaTutoDemo
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;在這裡面建一個目錄。&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;d:\Temps\JavaTutoDemo\bookstore
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;請打開你剛準備的文字篇輯器，開始敲程式碼了。 產生一個名為&lt;code&gt;BookStore.java&lt;/code&gt;的檔案。 敲入以下程式碼；然後以&lt;strong&gt;UTF-8 without BOM&lt;/strong&gt;的格式存到bookstore目錄下。&lt;pre class=&quot;prettyprint linenums&quot;&gt;&lt;code&gt;package bookstore;
public class BookStore{
public static void main(String[] args){
 System.out.println(&amp;quot;Hello Java&amp;quot;);
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;使用命令列模式，到工作本位目錄(JavaTutoDemo)，執行以下命令：&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;d:\Temps\JavaTutoDemo&amp;gt;javac -encoding utf-8 bookstore\BookStore.java
d:\Temps\JavaTutoDemo&amp;gt;java bookstore.BookStore
Hello Java
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;h2 id=&quot;-&quot;&gt;學而不知則罔&lt;/h2&gt;&lt;p&gt;繼之前的三個小之後，還是需要講解一下你剛才到底打了些什麼東西。 雖然我並不是很喜歡古文的那些之乎則也，也覺得那些早該作古的東西，十之八九不應該還存在在這個世界，不過有時候，還真的必須佩服那些古人，可以在那麼久遠的年代，就發現那些即使是在現在，也還應該存在於這個世界的那些十之一二的部份。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://c1.staticflickr.com/9/8883/17654703618_d64f6092d0_o.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;圖片引用自&lt;a href=&quot;http://www.23yy.com&quot;&gt;http://www.23yy.com&lt;/a&gt;&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;檔案BookStore.java的第一行，是package的宣告。 所謂的package，你可以將他視為一群程式。將相同性質的程式放在一塊，就是一個package。 &lt;strong&gt;Package名稱必需跟目錄名稱相同&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;第二是行宣告一個類別。 有關於類別，未來還會再談到更多。 現在，你就當類別…就是一個類別。 &lt;code&gt;class&lt;/code&gt;是關鍵字； 說明我正在宣告一個類別。&lt;strong&gt;類別名稱必需跟檔案名稱相同&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;第三行，是宣告一個函式。以Java的專用術語，我們叫&lt;strong&gt;method&lt;/strong&gt;。C、其他的程式語言、或是計算機概論中，稱為函式或叫function；在目前你可以先將他視為是相同的東西，不過在物件導向中，這是有很大的差別。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;static&lt;/code&gt; 是關鍵字，有著特殊的意思，他代表著這個method應該是由class來執行。我在之後的文章裡會再詳細提到這點。 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;String&lt;/code&gt; 在這裡是一個種資料型別。在Java裡面，他當然是一種class，而這個class的名稱叫做&lt;code&gt;String&lt;/code&gt;。 不過我們在這裡，就先把他視為是一個字串吧！ 就好像 &amp;quot;This is a string&amp;quot; 這樣的一個字串。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;[]&lt;/code&gt; 是表示陣列，所以&lt;code&gt;String[]&lt;/code&gt;就是字串陣列的意思。 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;所以， 這一行的意思是說，我宣告一個static的method，名字叫做&lt;code&gt;main&lt;/code&gt;，他有1個輸入參數，參數名稱叫&lt;code&gt;args&lt;/code&gt;，型態是&lt;code&gt;String[]&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;第四行執行的動作，就是列印出一個&amp;quot;Hello Java&amp;quot;字串。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在第4動，執行了2個命令。 第一個命令，是產生執行檔。 java&lt;strong&gt;c&lt;/strong&gt;的c 是Compilier的意思。 你可以看bookstore目錄裡，多了一個&lt;code&gt;BookStore.class&lt;/code&gt;的檔案。Java在執行時就是執行這個檔案。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;第四動的第二個令命則是直接執行這個執行檔。 注意，這裡不能加class副檔名。 從作業系統執行的method，永遠是static method，而且名稱永遠叫&lt;code&gt;main&lt;/code&gt;。這也就是為什麼我們可以直接由作業系統直接執行這個Hello world的原因。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;h1 id=&quot;-&quot;&gt;前面我沒有提到的&lt;/h1&gt;&lt;p&gt;前面我沒有提到的是：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Java的世界裡是區分大小寫的。如果你在練習的當中有發生任何錯誤，確定一下是不是大小寫有打錯…。&lt;/li&gt;
&lt;li&gt;開發慣例上，package的名稱一律都是小寫。Class是以大寫開頭的Camel style。例如&lt;code&gt;BookStore&lt;/code&gt;、&lt;code&gt;HelloWorld&lt;/code&gt;。Method以小寫開的的Camel style，例如&lt;code&gt;buyBook&lt;/code&gt;、&lt;code&gt;takeOrder&lt;/code&gt;或是&lt;code&gt;order&lt;/code&gt;。 &lt;/li&gt;
&lt;/ol&gt;&lt;h1 id=&quot;-&quot;&gt;結語&lt;/h1&gt;&lt;p&gt;到目前為止，你已經做出一個沒啥用處Hello Java程式。 僅管程式本身沒啥用處，但是裡面所隱藏的知識不少。 你應該知這了CLASSPATH、PATH、JAVA_HOME的用處、還有建立一個java程式的文法、結構、與宣告方式。 如果想學好一門程式語言，最好的方法，還是要實際下去演練一遍。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/6192028703147846037/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/6192028703147846037' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/6192028703147846037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/6192028703147846037'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/05/Java101-HelloWorld.html' title='你的第一隻Java程式─Hello World'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-5725945381170354148</id><published>2015-05-13T14:43:00.000+08:00</published><updated>2015-05-13T14:43:03.643+08:00</updated><title type='text'>物件的具現─如何將物件存成檔案</title><content type='html'>&lt;p&gt;Java裡有一種物件叫&lt;code&gt;java.io.Serializable&lt;/code&gt;，是可以讓物件在Serialize後在不同的儲存體中傳遞。中文譯做&lt;strong&gt;序列化&lt;/strong&gt;。 今天講講怎麼麼將物件存到硬碟這個儲存體…&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;假設我有一個物件是這樣：他是一個&lt;code&gt;Serializable&lt;/code&gt;的物件。 裡面有一個&lt;code&gt;Collection&lt;/code&gt;(datas)，可以用來存放&lt;code&gt;Serializable&lt;/code&gt;的物件。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;public class FileDatas implements Serializable {
    private static final long serialVersionUID = 8048736089512288707L;
    public Collection&amp;lt;Serializable&amp;gt; datas;
    public FileDatas() {
        datas = new HashSet&amp;lt;Serializable&amp;gt;();
    }
    public Collection&amp;lt;Serializable&amp;gt; getDatas() {
        return datas;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;與另外一個Serializable物件&lt;code&gt;Book&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;public class Book implements Serializable {
    private static final long serialVersionUID = -4088035671812463272L;
    private String isbn;
    private String author;
    private String name;
    public Book() {
    }
    // Other gets and setters.
    @Override
    public String toString() {
        return &amp;quot;Book [name=&amp;quot; + name + &amp;quot;]&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;於是我要將&lt;code&gt;Book&lt;/code&gt;這個物件存為實體檔案與取得的方式，大概是這樣：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;package taichitech.common.db;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.util.Collection;

import org.apache.commons.lang3.SerializationUtils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileDatasTest {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Test
    public void testSetDatas() {
        try (FileOutputStream fout = new FileOutputStream(filePath);) {
            FileDatas fd = new FileDatas();
            Collection&amp;lt;Serializable&amp;gt; datas = fd.getDatas();
            datas.add(newExampleBook(&amp;quot;994-443-989&amp;quot;, &amp;quot;Tom Edsion&amp;quot;, &amp;quot;How to Generate Java Class?&amp;quot;));
            datas.add(newExampleBook(&amp;quot;907-43-169&amp;quot;, &amp;quot;Willion Smith&amp;quot;, &amp;quot;Eclipse Pro, 2Ed&amp;quot;));
            datas.add(newExampleBook(&amp;quot;659-771-01&amp;quot;, &amp;quot;Water 13&amp;quot;, &amp;quot;如何銷售你自己&amp;quot;));
            logger.debug(&amp;quot;Books {}&amp;quot;, datas);
            SerializationUtils.serialize(fd, fout);
        } catch (Exception e) {
            logger.error(&amp;quot;Fial&amp;quot;, e);
        }
    }

    private Book newExampleBook(String isbn, String author, String name) {
        Book b = new Book();
        b.setAuthor(author);
        b.setIsbn(isbn);
        b.setName(name);
        return b;
    }

    private final String filePath = &amp;quot;.filedatas.ser&amp;quot;;

    @Test
    public void testGetDatas() {
        try (FileInputStream fin = new FileInputStream(filePath);) {
            FileDatas fd = SerializationUtils.deserialize(fin);
            logger.debug(&amp;quot;Books {}&amp;quot;, fd.getDatas());
        } catch (Exception e) {
            logger.error(&amp;quot;Fial&amp;quot;, e);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;testGetDatas&lt;/code&gt;示範了如何將物件存成檔案，而&lt;code&gt;testGetDatas&lt;/code&gt;示範如何將檔案再轉回物件。&lt;/p&gt;&lt;p&gt;這裡我主要是使用Apache commons-lang3的&lt;code&gt;SerializationUtils&lt;/code&gt;這個物件。 其實&lt;code&gt;deserialize()&lt;/code&gt;或&lt;code&gt;serialize()&lt;/code&gt;裡面就是將Object 轉成&lt;code&gt;ObjectInputStream&lt;/code&gt;或&lt;code&gt;ObjectOutputStream&lt;/code&gt;。 &lt;/p&gt;&lt;p&gt;延申閱讀：&lt;/p&gt;&lt;p&gt;你可以參考&lt;a href=&quot;http://taichitech.blogspot.tw/2007/09/serializablebyte.html&quot;&gt;如何轉Serializable物件轉成byte[]&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;&lt;em&gt;毫無疑問的，打算要存成檔案的物件，都必需要是Serialiazble，所以Collection裡的物件，或是相關property的型別，都必需要是Serializable。&lt;/em&gt;&lt;/p&gt;&lt;p&gt;2個UT測試的執行結果如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;10:38:11.569 [main] DEBUG t.c.db.FileDatasTest[24]-Books [Book [name=Eclipse Pro, 2Ed], Book [name=How to Generate Java Class?], Book [name=如何銷售你自己]]

11:31:45.541 [main] DEBUG t.c.db.FileDatasTest[45]-Books [Book [name=How to Generate Java Class?], Book [name=如何銷售你自己], Book [name=Eclipse Pro, 2Ed]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;儲存的檔案，用文字篇輯器打開會是這個樣子：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://c2.staticflickr.com/6/5450/16962212474_d807dc5b95_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;這個檔案存了3個&lt;code&gt;Book&lt;/code&gt; 物件與一個&lt;code&gt;FileDatas&lt;/code&gt;物件&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h1 id=&quot;-&quot;&gt;同場加印&lt;/h1&gt;&lt;p&gt;我在unit test中產生&lt;code&gt;Book&lt;/code&gt;物件的方式就是&lt;code&gt;Factory Method&lt;/code&gt;的應用。 &lt;/p&gt;&lt;p&gt;延申閱讀：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://taichitech.blogspot.tw/2015/03/design-pattern-102-simple-factory.html&quot;&gt;Design Pattern 102: Factory Method&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/5725945381170354148/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/5725945381170354148' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/5725945381170354148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/5725945381170354148'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/05/HowToSaveObjectAsFile.html' title='物件的具現─如何將物件存成檔案'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-273864096652387259</id><published>2015-05-13T01:31:00.000+08:00</published><updated>2015-05-13T01:31:42.872+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Java 101-Overview</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://c2.staticflickr.com/6/5441/16942670574_4678edd08f_z.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;基本上我是寫Java 的。就先對Java來做個介紹。&lt;/p&gt;&lt;p&gt;其實這個題目是很有挑戰性。 挑戰在於需要在很短的篇幅、時間為我很熟悉的東西做介紹。&lt;a name=&#39;more&#39;&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;Java 是昇陽電腦公司的詹姆斯·高斯林等人於1990開發的一種&lt;strong&gt;物件導向語言&lt;/strong&gt;。他屬於&lt;strong&gt;直譯式&lt;/strong&gt;語言。具有跨平台的特色 。&lt;/p&gt;&lt;p&gt;(我真的不適合當行銷人員呀，真的不會寫文案…)&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;模組&lt;/h1&gt;&lt;p&gt;接下來是重點了。&lt;/p&gt;&lt;p&gt;Java的版本，分為3個版本：Java SE(Java Standard Edition)、Java EE(Java Enterprise Edition)、Java ME(Java Mobile Edition)。 SE是所有基礎中的基礎，EE是專為企業所開發出來的架構，ME是為移動裝置所開發出來的架構。 執行上，Java 分為JRE(&lt;em&gt;J&lt;/em&gt;ava &lt;em&gt;R&lt;/em&gt;un time &lt;em&gt;E&lt;/em&gt;nvironment)與JDK(&lt;em&gt;J&lt;/em&gt;ava &lt;em&gt;D&lt;/em&gt;evelop took &lt;em&gt;K&lt;/em&gt;it)。JDK包含了許許多多的開發套件；所有開發出來的程式，都必需在JRE上執行。 就某種角度來看Java雖然宣稱跨平台，但其實不是；因為他只能在JRE這個平台上面執行。 真正跨平台的是JRE。&lt;/p&gt;&lt;p&gt;以下我們先針對Java SE 與Java EE這兩塊來做介紹。&lt;/p&gt;&lt;h1 id=&quot;java-se&quot;&gt;Java SE&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;http://c4.staticflickr.com/8/7771/17375004950_fa09e420bf_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font style=&quot;font-size:small&quot;&gt;Java SE的模組&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Java SE 的模組就是以上這些。 眼花撩亂對吧，沒關係，我先提出幾個部份出來。這幾個部份是非常、非常、非常、常用到(很重要，所以要講3次)，未來堂學習的時候，必需要特別熟悉。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Lang and util&lt;/li&gt;&lt;li&gt;Collections&lt;/li&gt;&lt;li&gt;Input/Output&lt;/li&gt;&lt;li&gt;JDBC&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;當然有很多是時常在用，可是你不知道你在用的東西。這些東西在「很」未來還是需要知道，不過現在，就先在此打住吧！&lt;/p&gt;&lt;h1 id=&quot;java-ee&quot;&gt;Java EE&lt;/h1&gt;&lt;p&gt;Java EE 其實我沒法介紹太多，因為有許多我並沒有接觸過。 他基本上是由一系列的規格所兜出來的。 在這裡將所有的規格名詞一口氣打上來，也同樣的會讓人眼花撩亂，而且意義也不太大。 我講幾個重要的名詞吧！ 不過這些名詞並不是Java EE 的規格名稱，而是在未來在學習Java EE時所必需要知道的。&lt;/p&gt;&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;詞&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Servlet&lt;/td&gt;
&lt;td&gt;在網際網上執行的Java 程式。也許這樣的定義不是很正確，但我想初學者先這樣看待servlet就好。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Container&lt;/td&gt;
&lt;td&gt;一個可以執行Servlet的環境&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EJB Container&lt;/td&gt;
&lt;td&gt;Web Container的進階版。除了可以執行一般的servlet，還可以架設web service，設置一堆Java bean、Pool、有的沒的。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSP&lt;/td&gt;
&lt;td&gt;Java 版的ASP。如果你不知道什麼是ASP，那你就把他當做是網頁的頁面。 同樣的，這樣定義也不盡然正確，不過也是先這樣看待就好&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EJB&lt;/td&gt;
&lt;td&gt;Enterprise Java Bean，運行在EJB Container上的一些Java bean。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;h1 id=&quot;bean&quot;&gt;Bean&lt;/h1&gt;&lt;p&gt;有個名詞在上面提到很多次，不過到目前為止，我還沒有對他做介紹，那就是&lt;code&gt;Bean&lt;/code&gt;。 &lt;code&gt;Bean&lt;/code&gt;是Java中，有著特殊定義的一種類別。 官方定義如下：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;有一個公有的無參建構元。&lt;/li&gt;&lt;li&gt;屬性可以通過get、set、is（可以替代get，用在布爾型屬性上）方法或遵循特定命名規範的其他方法訪問。&lt;/li&gt;&lt;li&gt;可序列化。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;這裡是一個&lt;code&gt;bean&lt;/code&gt;的範例。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public class FaceBean {
    private int mMouthWidth = 90;

    public FaceBean(){};

    public int getMouthWidth() {
        return mMouthWidth;
    }

    public void setMouthWidth(int mw) {
        mMouthWidth = mw;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-&quot;&gt;結語&lt;/h1&gt;&lt;p&gt;這篇文章大概介紹了一下Java 的生態，讓大家稍微了解一下什麼是Java。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/273864096652387259/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/273864096652387259' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/273864096652387259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/273864096652387259'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/05/java-overview.html' title='Java 101-Overview'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-7813309493829377126</id><published>2015-05-10T01:15:00.000+08:00</published><updated>2015-05-10T01:15:03.075+08:00</updated><title type='text'>Struts1昇級有問題？ 如何將.do導頁到.action</title><content type='html'>&lt;h1 id=&quot;-&quot;&gt;緣起&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7670/17156305590_874b4ca070_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;Struts 是一個老牌的Java web framework。有多老，老到Struts1 已經停止維護了。 Struts2看似是Struts1的延伸，不過Struts2是不向下相容於Struts1。 所謂的&lt;em&gt;不向下相容&lt;/em&gt;，並不是指Struts1與Struts2 framework不能同時存在於一個web application裡面，而是&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;使用struts1寫的程式，沒有辦法僅僅拿掉struts1的jar檔、加上struts2的jar檔，&lt;code&gt;web.xml&lt;/code&gt;設一設就可以用了。 那些原本struts1寫的程式，還是需要進行相當程度的改寫。 除了將struts1的程式改為使用struts2的架構外，許許多多的連結也需要換掉。 因為struts1預設副檔名是使用&lt;code&gt;.do&lt;/code&gt;，struts2的副檔名是使用&lt;code&gt;.action&lt;/code&gt;，在昇級的時候，如果不是一次到位，就會導致系統裡面，Struts1的jar檔與Struts2的jar檔同時存在，&lt;code&gt;.do&lt;/code&gt;的連結指向struts1的功能，&lt;code&gt;.action&lt;/code&gt;的連結就會使用struts2的功能。 然而在一個已經上線的系統上，也似乎必需得這麼做，讓系統一支作業一支作業的昇級...&lt;/p&gt;&lt;p&gt;現在問題來了。 如果一支作業已經由struts1昇級到struts2，但是我們無法確認外部系統對內的所有的.do連結，是否已經全部換成.action。 一個折衷的辦法，就是讓.do的連結再forward到.action。 這就是我們今天要講的，如何讓指向struts1的.do連結導到struts2的.action。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;請注意，以下方法適用於servlet 2.4以上 &lt;/strong&gt;。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;如何做？&lt;/h1&gt;&lt;p&gt;Struts2 是使用filter的方式來install的，所以我們唯一要做的，只要在web.xml的地方增加forward dispatcher；struts1的action則直接使用config的方式forward到struts2的連結，就可以讓struts2在forward時觸發。&lt;/p&gt;&lt;p&gt;struts-config.xml.&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;action path=&amp;quot;/struts1moreaction&amp;quot; forward=&amp;quot;/struts2action.action&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;strus.xml&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;package name=&amp;quot;taichitech&amp;quot; extends=&amp;quot;struts-default&amp;quot; namespace=&amp;quot;/&amp;quot; &amp;gt;
    &amp;lt;action name=&amp;quot;struts2action&amp;quot; class=&amp;quot;taichitech.struts1to2.Struts2Action&amp;quot; method=&amp;quot;execute&amp;quot;&amp;gt;
        &amp;lt;result name=&amp;quot;success&amp;quot;&amp;gt;/submitted2.jsp&amp;lt;/result&amp;gt;
    &amp;lt;/action&amp;gt;
&amp;lt;/package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;web.xml&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;struts2&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;*.action&amp;lt;/url-pattern&amp;gt;
    &amp;lt;dispatcher&amp;gt;FORWARD&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;REQUEST&amp;lt;/dispatcher&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-&quot;&gt;我剛看了小小小？&lt;/h1&gt;&lt;p&gt;如果看不懂到目前為止的程式碼，接下來我會有更詳細的描述。&lt;/p&gt;&lt;p&gt;基本上，一個系統的架設可以有很多種方法。我這裡demo的是使用Maven的方法建立的。 當然，你也可以自己直接手動將jar檔放入相關的位置。&lt;/p&gt;&lt;h2 id=&quot;dependency&quot;&gt;Dependency&lt;/h2&gt;&lt;p&gt;Struts1 與Struts2所使相關的lib檔，可以透過Maven直接設定取得。 Pom.xml的設定是這樣的：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.struts&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;struts2-core&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.3.16.3&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.struts&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;struts-core&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.3.10&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.struts&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;struts-extras&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.3.10&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.struts&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;struts-taglib&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.3.10&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;web-xml&quot;&gt;Web.xml&lt;/h2&gt;&lt;p&gt;Struts framework(不管是1或2)都有龐大的功能，與相對應的組態。這裡展示最基本的設定。這個&lt;code&gt;web.xml&lt;/code&gt;同時設定了 Struts1與Struts2。 其實Struts1與Struts2是可以同時放在相同一個app裡的。&lt;/p&gt;&lt;p&gt;web.xml&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;web-app xmlns=&amp;quot;http://java.sun.com/xml/ns/javaee&amp;quot; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;
    xsi:schemaLocation=&amp;quot;http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&amp;quot;
    version=&amp;quot;2.5&amp;quot;&amp;gt;
    &amp;lt;display-name&amp;gt;Servlet 2.5 Web Application&amp;lt;/display-name&amp;gt;
    &amp;lt;filter&amp;gt;
    &amp;lt;filter-name&amp;gt;struts2&amp;lt;/filter-name&amp;gt;
    &amp;lt;filter-class&amp;gt;org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter&amp;lt;/filter-class&amp;gt;
  &amp;lt;/filter&amp;gt;
  &amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;struts2&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;*.action&amp;lt;/url-pattern&amp;gt;
&amp;lt;!-- Here is point --&amp;gt;
    &amp;lt;dispatcher&amp;gt;FORWARD&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;REQUEST&amp;lt;/dispatcher&amp;gt;
  &amp;lt;/filter-mapping&amp;gt;
  &amp;lt;servlet&amp;gt;
    &amp;lt;servlet-name&amp;gt;action&amp;lt;/servlet-name&amp;gt;
    &amp;lt;servlet-class&amp;gt;org.apache.struts.action.ActionServlet
         &amp;lt;/servlet-class&amp;gt;
    &amp;lt;init-param&amp;gt;
      &amp;lt;param-name&amp;gt;config&amp;lt;/param-name&amp;gt;
      &amp;lt;param-value&amp;gt;/WEB-INF/classes/struts-config.xml&amp;lt;/param-value&amp;gt;
    &amp;lt;/init-param&amp;gt;
  &amp;lt;/servlet&amp;gt;
  &amp;lt;servlet-mapping&amp;gt;
    &amp;lt;servlet-name&amp;gt;action&amp;lt;/servlet-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;*.do&amp;lt;/url-pattern&amp;gt;
  &amp;lt;/servlet-mapping&amp;gt;
&amp;lt;/web-app&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;請注意struts2的那個&lt;code&gt;filter-mapping&lt;/code&gt;。在一般的教學中，struts2的設定檔都不會告訴你需要使用&lt;code&gt;&amp;lt;dispatcher&amp;gt;&lt;/code&gt;。這是servlet 2.4以後的規格。 預設是&lt;code&gt;REQUEST&lt;/code&gt;所以在一般的情況下，是不需要額外設定。 不過我們這裡的策略是讓struts1的url link(也就是&lt;code&gt;*.do&lt;/code&gt;) 經過struts1 framework後再&lt;strong&gt;forward&lt;/strong&gt;到 struts2 framework，所以需要再額外指定&lt;code&gt;&amp;lt;dispatcher&amp;gt;FORWARD&amp;lt;/dispatcher&amp;gt;&lt;/code&gt;讓struts2的filter可以在forward的時候被觸發。&lt;/p&gt;&lt;h1 id=&quot;struts-config-xml&quot;&gt;struts-config.xml&lt;/h1&gt;&lt;p&gt;這是struts1的預設設定檔。 它長的像是這個樣子。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE struts-config PUBLIC &amp;quot;-//Apache Software Foundation//DTD Struts Configuration 1.2//EN&amp;quot; &amp;quot;http://struts.apache.org/dtds/struts-config_1_2.dtd&amp;quot;&amp;gt;
&amp;lt;struts-config&amp;gt;
    &amp;lt;action-mappings&amp;gt;
        &amp;lt;action path=&amp;quot;/struts1action&amp;quot; type=&amp;quot;taichitech.struts1to2.Struts1Action&amp;quot;
            parameter=&amp;quot;method&amp;quot; scope=&amp;quot;request&amp;quot;&amp;gt;
            &amp;lt;forward name=&amp;quot;success&amp;quot; path=&amp;quot;/struts2action.action&amp;quot; /&amp;gt;
        &amp;lt;/action&amp;gt;
        &amp;lt;action path=&amp;quot;/struts1moreaction&amp;quot; forward=&amp;quot;/struts2action.action&amp;quot; /&amp;gt;
    &amp;lt;/action-mappings&amp;gt;
&amp;lt;/struts-config&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我示範了2種手法。 第一種，仍然經過struts1 action。 不過&lt;code&gt;success&lt;/code&gt;這個&lt;code&gt;ActionForward&lt;/code&gt;的&lt;code&gt;path&lt;/code&gt;是指向struts2的url。看，url的後綴是&lt;strong&gt;.action&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;第2個更省事，連struts1的class也不用，直接forward到struts2。 &lt;code&gt;Action&lt;/code&gt;這個tag，什麼也沒做，甚至連class也不指定了，直接forward 到struts2的url。&lt;/p&gt;&lt;h1 id=&quot;struts-xml&quot;&gt;struts.xml&lt;/h1&gt;&lt;p&gt;Struts2的設定檔，預設檔名是struts.xml。這與一般教學是一樣的，沒什麼特別的差異。 最後會導到&lt;code&gt;/submitted2.jsp&lt;/code&gt;。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;
&amp;lt;!DOCTYPE struts PUBLIC
    &amp;quot;-//Apache Software Foundation//DTD Struts Configuration 2.0//EN&amp;quot;
    &amp;quot;http://struts.apache.org/dtds/struts-2.0.dtd&amp;quot;&amp;gt;
&amp;lt;struts&amp;gt;
    &amp;lt;constant name=&amp;quot;struts.devMode&amp;quot; value=&amp;quot;false&amp;quot; /&amp;gt;
    &amp;lt;constant name=&amp;quot;struts.ui.theme&amp;quot; value=&amp;quot;simple&amp;quot; /&amp;gt;
    &amp;lt;package name=&amp;quot;taichitech&amp;quot; extends=&amp;quot;struts-default&amp;quot; namespace=&amp;quot;/&amp;quot; &amp;gt;
        &amp;lt;action name=&amp;quot;struts2action&amp;quot; class=&amp;quot;taichitech.struts1to2.Struts2Action&amp;quot; method=&amp;quot;execute&amp;quot;&amp;gt;
            &amp;lt;result name=&amp;quot;success&amp;quot;&amp;gt;/submitted2.jsp&amp;lt;/result&amp;gt;
        &amp;lt;/action&amp;gt;
    &amp;lt;/package&amp;gt;      
&amp;lt;/struts&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;action&quot;&gt;Action&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Struts1&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Struts1 的action 範例：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public class Struts1Action extends DispatchAction {

    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        Logger logger = LoggerFactory.getLogger(getClass());
        logger.debug(&amp;quot;Execute Struts1Action.execute&amp;quot;);
        return mapping.findForward(&amp;quot;success&amp;quot;);

    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;Struts2&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Struts2 action的範例：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public class Struts2Action extends BaseAction {
    public String execute() {
        Logger logger = LoggerFactory.getLogger(getClass());
        logger.debug(&amp;quot;Execute Struts2Action.execute&amp;quot;);
        return Action.SUCCESS;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;index.jsp&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;body&amp;gt;
    &amp;lt;a href=&amp;quot;${pageContext.request.contextPath}/struts1action.do&amp;quot;&amp;gt;Here
        is an .do link-&amp;lt;/a&amp;gt; &amp;lt;br/&amp;gt;
    &amp;lt;a href=&amp;quot;${pageContext.request.contextPath}/struts1moreaction.do&amp;quot;&amp;gt;Here
        is an more .do link-&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;http://c1.staticflickr.com/9/8797/17270592968_7d0dc6b624_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;submitted2.jsp&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;body&amp;gt;A page that submitted!!
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;http://c4.staticflickr.com/8/7674/16835818554_6a7c3a1fb4_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;由於是採用forward的方式，url還是&lt;code&gt;.do&lt;/code&gt;。&lt;/em&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;結論&lt;/h1&gt;&lt;p&gt;在系統改版時，可以一次到位，將所有的struts1的action重新改成struts2、struts1的jar檔可以一次被抽換掉，當然是最好。 如果不行的話，這樣的一個折衷的保險措施是必需要的。 當然一個系統的昇版並不是那麼簡單，還有許多的的部份，例如&lt;code&gt;action form&lt;/code&gt;如何轉換、&lt;code&gt;validation&lt;/code&gt;、&lt;code&gt;action error&lt;/code&gt;、&lt;code&gt;action message&lt;/code&gt;如何修改，這些都是考驗著我們的智慧。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/7813309493829377126/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/7813309493829377126' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7813309493829377126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7813309493829377126'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/05/struts1-doaction.html' title='Struts1昇級有問題？ 如何將.do導頁到.action'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-6710303440076277022</id><published>2015-05-06T17:58:00.000+08:00</published><updated>2015-06-01T10:29:36.794+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design Pattern"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Design Pattern 介紹：Memento</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8781/17354067911_844a6ae6eb_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font size=&quot;small&quot;&gt;Memento design pattern的class diagram。圖片取自Wiki&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Memento 這個pattern的使用範圍很明確，就是當你有rollback、undo這類需求的時候的時候，都可以使用。我們透個一個情境來介紹一下memento這個pattern 。&lt;/p&gt;&lt;p&gt;Ok，情境是這樣的…&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;你的需求是一個交易(Transaction)。使用者買東西時，會從帳號(Account)裡扣錢，每次扣1000元。我今天有一段很無聊(？)的程式(demoMemento)，扣了2次錢之後，使用者硬是要rollback回去，當交易沒發生過。所以你必需將帳號的狀態設會原本扣2次錢之前的狀態。`&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8892/17150727487_6331db560c_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;font size=&quot;small&quot; &gt;Memento design pattern情境的 class diagram &lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;以下是&lt;code&gt;RunDp.demoMemento()&lt;/code&gt;的程式。這一段是主程式。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;package taichitech.jblog.dp;

public class RunDp {
    Logger logger = LoggerFactory.getLogger(getClass());

    public RunDp() {

    public static void main(String[] args) {
        RunDp rd = new RunDp();
        rd.demoMemento();
    }

    public void demoMemento() {
        AccountDao accountDao = new AccountDao();
        String accountId = &amp;quot;99004-PETER-22-114&amp;quot;;
        int amount = -10000;
        //Get account from Dao and deduct amount x 2. 
        Account account = accountDao.findAccount(accountId);
        //Before deduct, we create a memento.
        account.createMemnto();
        account.setBalance(account.getBalance() + amount);
        logger.debug(&amp;quot;Account info-1:{}&amp;quot;, account);
        account.createMemnto();
        account.setBalance(account.getBalance() + amount);
        logger.debug(&amp;quot;Account info-2:{}&amp;quot;, account);
        account.restoreMemnto(2);
        logger.debug(&amp;quot;Rollbacked Account info:{}&amp;quot;, account);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;main&lt;/code&gt;那些什麼的都不是我們要講的重點；&lt;code&gt;demoMemento()&lt;/code&gt;才是主程式的重點。&lt;/p&gt;&lt;p&gt;程式一開始產生了一個&lt;code&gt;AccountDao&lt;/code&gt;，超顧名思義的，就是可以透過一個&lt;code&gt;accountId&lt;/code&gt;取得一個&lt;code&gt;Account&lt;/code&gt;物件。&lt;/p&gt;&lt;p&gt;&lt;code&gt;Account&lt;/code&gt;物件可以做3件事：1. 建立memento(&lt;code&gt;createMemnto()&lt;/code&gt;)，2. 扣款(&lt;code&gt;Account.setBalane()&lt;/code&gt;)，3. 回復狀態(&lt;code&gt;restoreMemnto()&lt;/code&gt;)。&lt;/p&gt;&lt;p&gt;&lt;code&gt;RunDp&lt;/code&gt;取得&lt;code&gt;Account&lt;/code&gt;後，首先建立了一個memento、然後扣款(-10000)、印出訊息；這樣的動作一共做了2次。然後使用者&lt;del&gt;閒東西太貴&lt;/del&gt;又想取消交易，於是我們只好將狀態回到2次交易前的樣子(&lt;code&gt;restoreMemnto(2)&lt;/code&gt;)。&lt;/p&gt;&lt;p&gt;接下來我們看一下&lt;code&gt;Account&lt;/code&gt;，這部份才是我們要提的memento重點：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;package taichitech.jblog.dp.vo;

public class Account {

    @Override
    public String toString() {
        return &amp;quot;Account [accountId=&amp;quot; + accountId + &amp;quot;, balance=&amp;quot; + balance + &amp;quot;, snapshot=&amp;quot; + snapshots + &amp;quot;]&amp;quot;;
    }

    private String accountId;

    private int balance;

//...Other getter and setter of properties.

    private ArrayList&amp;lt;Memento&amp;gt; snapshots = new ArrayList&amp;lt;&amp;gt;();

    private class Memento {
        int balance;

        public int getBalance() {
            return balance;
        }

        public void setBalance(int balance) {
            this.balance = balance;
        }
        @Override
        public String toString() {
            return &amp;quot;Memento [balance=&amp;quot; + balance + &amp;quot;]&amp;quot;;
        }
    }

    public void createMemnto() {
        Memento memento = new Memento();
        memento.setBalance(this.balance);
        snapshots.add(memento);
    }

    /** @param undoTimes Times of undo. */
    public void restoreMemnto(int undoTimes) {
        undoTimes = (snapshots.size() &amp;lt; undoTimes ? snapshots.size() : undoTimes);
        for (int i = 0; i &amp;lt; undoTimes; i++) {
            int lastIndex = snapshots.size() - 1;
            this.balance = snapshots.get(lastIndex).getBalance();
            snapshots.remove(lastIndex);
        }
    }
`}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Account除了記錄餘額外，裡面還有一個snapshots的property。 他是一個&lt;code&gt;Mement&lt;/code&gt; class 的集合(&lt;code&gt;ArrayList&lt;/code&gt;)，這是用來記錄每次的狀態，每&lt;code&gt;createMemnto()&lt;/code&gt;一次，就會產生一個&lt;code&gt;Memento&lt;/code&gt;物件加到集合裡。&lt;/p&gt;&lt;p&gt;&lt;code&gt;Memento&lt;/code&gt; class是一個innter class。使用innter class主要是基於權限的關係。RunDp或是其他的Programmer、維運人員，可以完全不用知道裡面的狀況。 這樣除了可以避免不小心改到不相關的程式碼外，也可以避免混淆視聽。 當然，你也可以把&lt;code&gt;Memento&lt;/code&gt;開放出來，讓RunDp、或是其他人員進行&lt;em&gt;其他&lt;/em&gt;的操作。 這裡指的&lt;em&gt;其他&lt;/em&gt; 不應該包含會影響到&lt;code&gt;Account&lt;/code&gt;原本狀態的操作。 有關於&lt;code&gt;Account&lt;/code&gt;狀態的一些操作，應該將權限綁成全部由&lt;code&gt;Account&lt;/code&gt;來進行操作。&lt;/p&gt;&lt;p&gt;另外一個&lt;code&gt;restoreMemnto()&lt;/code&gt; 是將&lt;code&gt;Account&lt;/code&gt;的狀態回復。參數是回復次數，可以回復前&lt;code&gt;undoTimes&lt;/code&gt;次。裡面的迴圈，會依次取得最後一次&lt;code&gt;Memento&lt;/code&gt;的物件，然後將資料回設給&lt;code&gt;Account&lt;/code&gt;。回設後會將該物件由&lt;code&gt;snapshots&lt;/code&gt;中拿掉。 當然，以design pattern來說，裡面的那些個回復、怎麼的回復，也不是重點；&lt;strong&gt;重點是memento design pattern 通常會有個method，以供回復狀態&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;在實作上，你可以直接指定&lt;code&gt;ArrayList.get(index)&lt;/code&gt;，取得特定的&lt;code&gt;Memento&lt;/code&gt;，然後移除該index後的所有元素，這樣看似會比較有效率一些；而要不要移除元素，這也完全看需求，你也可以保留做為歷史追蹤。 &lt;/p&gt;&lt;p&gt;這裡是執行結果：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;868 [main] DEBUG t.jblog.dp.RunDp[37]-Account info-1:Account [accountId=99004-PETER-22-114, balance=690000, snapshot=[Memento [balance=700000]]]
869 [main] DEBUG t.jblog.dp.RunDp[40]-Account info-2:Account [accountId=99004-PETER-22-114, balance=680000, snapshot=[Memento [balance=700000], Memento [balance=690000]]]
869 [main] DEBUG t.jblog.dp.RunDp[42]-Rollbacked Account info:Account [accountId=99004-PETER-22-114, balance=700000, snapshot=[]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;請注意，這裡的回復是指「回到原本的狀態」不是「將錢還回去」。這2種描述在實作上有很大的差別。 第一種是像demo程式，直接將狀態「設」回去。第2種則是你必需要記錄每次所扣的錢，然後一次一次「加」回去。也就是第2種描述，你必記錄的不見得是變更前的「狀態」，而是每次變更的「異動」，例如-10000。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;限制&lt;/h1&gt;&lt;ol&gt;&lt;li&gt;一個最明顯的問題，當然是記憶體的問題。每mementdo一次，就是一個記憶體；可以備份多少次、要備份那些狀態，當然要量「機」而為。 &lt;/li&gt;
&lt;li&gt;如果你將&lt;code&gt;Memento&lt;/code&gt; class 開放給&lt;code&gt;Account&lt;/code&gt;以外的類別使用，需要注意開放的範圍；不可以讓開放的操作影響到&lt;code&gt;Account&lt;/code&gt;的狀態。 像&lt;code&gt;Memento.getBalance()&lt;/code&gt;與&lt;code&gt;Memento.seBalance()&lt;/code&gt;就應該只能由&lt;code&gt;Account&lt;/code&gt;來呼叫，不能由&lt;code&gt;RunDp&lt;/code&gt;呼叫。&lt;/li&gt;
&lt;/ol&gt;&lt;h1 id=&quot;-&quot;&gt;結語&lt;/h1&gt;&lt;p&gt;在原著上，Memento 這個pattern有3個class：&lt;code&gt;Caretaker&lt;/code&gt;、&lt;code&gt;Originator&lt;/code&gt;、&lt;code&gt;Memento&lt;/code&gt;；對應到我們的demo code，分別是&lt;code&gt;RunDp&lt;/code&gt;、&lt;code&gt;Account&lt;/code&gt;、&lt;code&gt;Account$Memento&lt;/code&gt;(這是一個innter class)。 &lt;code&gt;Caretaker&lt;/code&gt;操作&lt;code&gt;Originator&lt;/code&gt;的方法，以決定什麼時候需要memento。 &lt;code&gt;Memento&lt;/code&gt;儲存&lt;code&gt;Originator&lt;/code&gt;的狀態。 &lt;code&gt;Caretaker&lt;/code&gt;可以操作&lt;code&gt;Memento&lt;/code&gt;，不過不應該操作&lt;code&gt;Orignator&lt;/code&gt;狀態的相關資訊；&lt;code&gt;Originator&lt;/code&gt;狀態的變更，只應交由&lt;code&gt;Originator&lt;/code&gt;來操作。 &lt;code&gt;Memento&lt;/code&gt;在儲存&lt;code&gt;Originator&lt;/code&gt;狀態時，可以有不同的儲存方式與應用方式，例如備份狀態，或是備份差異，當然，備份時也需要考慮到resource是否足夠或是resource 的釋放。&lt;/p&gt;&lt;p&gt;個人覺得使用機會是比factory method、facade 或是其他的pattern要來得小的多。 儘管如此，當有用上的時候，他還是很有用。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/6710303440076277022/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/6710303440076277022' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/6710303440076277022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/6710303440076277022'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/05/design-pattern-memento.html' title='Design Pattern 介紹：Memento'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-3661376382769881944</id><published>2015-03-31T02:11:00.000+08:00</published><updated>2015-03-31T13:49:58.184+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design Pattern"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Design Patter 103：輪流取得每個物件─Iterator簡介</title><content type='html'>&lt;p&gt;上一篇介紹了&lt;a href=&quot;http://taichitech.blogspot.tw/2015/03/design-pattern-102-simple-factory.html&quot;&gt;Simple factor pattern&lt;/a&gt;， 今天來介紹一下Iterator。&lt;br /&gt;
&lt;img src=&quot;http://farm9.staticflickr.com/8686/16792800898_811ee741e3_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;Iterator，中譯迭代。簡單來說，Iterator 就是對集合裡的每個原素進行處理。&lt;/p&gt;&lt;p&gt;如果你在演算法裡有看到「對XX裡的每個YY…」，那你通常都會用到這個pattern。 &lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;for each y in x
    do blablabla
end for;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Iterator pattern 是對集合裡的每個原素(&lt;strong&gt;element&lt;/strong&gt;)進行處理，因此他的構成的成份，第一個就是「集合」這個元件。(好像是廢話呢…&lt;img src=&quot;http://farm8.staticflickr.com/7651/16600384969_2614917f0f_o.png&quot; alt=&quot;&quot;&gt;) 第二個元件，是通常會有個指標，來記錄目前處理到那一個原素；第三個元件是需要處理一些事。程式在處理完那些事後，就將指標移往下一個原素。 第四個，就是這一段程式碼裡，通常會需要有個判斷式，來判斷是不是所有有東西都已經處理過。 &lt;/p&gt;&lt;p&gt;接下來看一下實際例子。&lt;/p&gt;&lt;p&gt;假設我一個書櫃(&lt;code&gt;BookShelf&lt;/code&gt;)，有書櫃就有書(&lt;code&gt;BookVo&lt;/code&gt;)…這次書是我們要講的重點了。&lt;/p&gt;&lt;p&gt;於是程式碼Iterator interface會是像這個樣子：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public interface Iterator&amp;lt;E&amp;gt; {
    boolean hasNext();
    E next();
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;由於我們要依次處理的是書(&lt;code&gt;BookVo&lt;/code&gt;)；所以實作這個interface的程式碼會是這個樣子。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;private class BookVoIterator implements Iterator&amp;lt;BookVo&amp;gt; {

        private int index = -1;

        @Override
        public boolean hasNext() {
            return index &amp;lt; size - 1;
        }

        @Override
        public BookVo next() {
            index++;
            return bookVos[index];
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;next()&lt;/code&gt;是裡含了二個元件：第一個是指標(這不是C語言中的指標)的移動。第二個是「處理」；它除了在每次iterate時會將指標移到下一個原素之外，還做了一個動作：生出&lt;code&gt;BookVo&lt;/code&gt;，並加以回傳。&lt;/p&gt;&lt;p&gt;&lt;code&gt;hasNext()&lt;/code&gt;是第四個元件，它負責判斷是否還有下一個需要處理的原素，少了這元件，就會淪落成無窮迴圈(雖然有時需要這麼做…)。 &lt;/p&gt;&lt;p&gt;那第一個元件呢？ 再下面。 &lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public class BookShelf implements Iterable&amp;lt;BookVo&amp;gt; {

    private BookVo[] bookVos;

    public BookVo findBook(String bookName) {
        for (Iterator&amp;lt;BookVo&amp;gt; it = iterator(); it.hasNext();) {
            BookVo bookVo = it.next();
            if (bookVo.getName().equals(bookName)) {
                return bookVo;
            }
        }
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我們之前提到第一個元件，就是集合。 在這個例子裡，書的集合就是書櫃(&lt;code&gt;BookShelf&lt;/code&gt;)。 先暫時別管Iteratorable這個interface； 這個interface現在還不是重點。 重要的是iterator的使用方式； 主要client端method(就是使用端、呼叫的程式)是&lt;code&gt;findBook&lt;/code&gt;。顧名思義，就是從書名找書；怎麼找？一本一本找。 &lt;em&gt;對書櫃裡的每一本書進行比對&lt;/em&gt;，直找到書名相同的書為止。 有注意到嗎？ 「&lt;em&gt;對XX裡的每個YY…&lt;/em&gt;」，我又用到這樣的描述了…。&lt;/p&gt;&lt;p&gt;總而言之Iterator的class diagram。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8743/16317742743_9e67d52595_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;要怎麼用？&lt;/h1&gt;&lt;p&gt;Java 1.5 有提供了enhance for loop的語法。 我還蠻常用這個。 主要是因為我認為電腦就是該執行重覆的工作；反正就是有一堆工作叫電腦去做，讓電腦針&lt;em&gt;對所有工作任務裡的每一個工作&lt;/em&gt;(對XX裡的每個YY…)，一項一項執行…。 在for each裡的集合，必需要實作Iterable interface。 因此，上面的&lt;code&gt;findBook&lt;/code&gt;可改寫&lt;code&gt;borrowBook&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public BookVo borrowBook(String bookName) {
    ShelfFactory boFactory = context.getBean(ShelfFactory.class);
    BookShelf bookShelf = boFactory.getBookShelf(&amp;quot;POMO&amp;quot;);
    for (BookVo book : bookShelf) {
        if(book.getName().equals(bookName)){
            return book;
        }
    }
    return null;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以看到&lt;code&gt;for (BookVo book : bookShelf)&lt;/code&gt;的寫法要比單純的for迴圈的方式，要更簡單了一些。&lt;/p&gt;&lt;p&gt;題外話，Java是自1.5後才提供enhance foreach。 不過還記得我們之前有提過，Design pattern是無關乎程式語言的。 即始Java 1.5才提供foreach這種語法，早在更早之前，就有其他的Iteratgr介面可供使用。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;結論&lt;/h1&gt;&lt;p&gt;這一篇是使用與Simple factory相同的例子做示範，簡單介紹一下iterator pattern。 Iterator不只是可以&lt;code&gt;next&lt;/code&gt;，也有可以&lt;code&gt;previous&lt;/code&gt;、也可以直接&lt;code&gt;first&lt;/code&gt;或&lt;code&gt;last&lt;/code&gt;。 沒錯，就是我們一般在網頁上最常看到的paging。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8752/16979586102_9e53d4bfbd_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;大家是否有注意到，我們常用的jdbc &lt;code&gt;ResultSet&lt;/code&gt;也有相同的味道喔！&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/3661376382769881944/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/3661376382769881944' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/3661376382769881944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/3661376382769881944'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/design-patter-103iterator.html' title='Design Patter 103：輪流取得每個物件─Iterator簡介'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-8783427506332552717</id><published>2015-03-29T20:52:00.003+08:00</published><updated>2015-05-03T04:01:38.285+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design Pattern"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Design Pattern 102: Factory Method</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7649/16751590279_e6096150c6_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div style=&quot;font-size:small&quot;&gt;這是factory method  pattern的class diagram&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;&lt;a href=&quot;http://taichitech.blogspot.tw/2015/03/design-pattern-101_24.html&quot;&gt;之前&lt;/a&gt;有稍微對Design Pattern做了一些描述，大概說明了一下什麼是design pattern。今天會講解一下其中的一種：&lt;strong&gt;factroy methody&lt;/strong&gt;...&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;factory method&lt;/code&gt; 主要是當需要產生複雜的物件的時候使用。 由於產生物件的過程複雜，封裝起來，讓使用物件的人不需要知道產生的過成，就可以取得這個物件。&lt;/p&gt;&lt;h1 id=&quot;-hello-world-&quot;&gt;沒什麼用的Hello World...&lt;/h1&gt;&lt;p&gt;比方說我有一個&lt;code&gt;書櫃&lt;/code&gt;的類別(&lt;code&gt;BookShelf&lt;/code&gt;)。有書櫃就有&lt;code&gt;書&lt;/code&gt;，不過書並不是我們這次要講的重點，所以我們跳過…&lt;img src=&quot;http://farm8.staticflickr.com/7651/16600384969_2614917f0f_o.png&quot; alt=&quot;&quot;&gt; 。&lt;/p&gt;&lt;p&gt;回到書櫃這個類別。&lt;/p&gt;&lt;p&gt;書櫃的建構過程如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;... 前面還有50行程式碼...
List&amp;lt;Book&amp;gt; books = bookDao.findBookBy(shelfId);
BookVo[] bookVos = new BookVo[books.size()];
... 假設這裡有100行程式碼...
BookShelf bookShelf = new BookShelf(bookVos);
Shelf shelf = shelfDao.findShelf(shelfId);
bookShelf.setShelf(shelf);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;於是我用一個function(在Java 稱為&lt;code&gt;method&lt;/code&gt;)將這麼多程式碼封裝起來，…&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public class ShelfFactory {
    public BookShelf getBookShelf(String shelfId) {
        ...前面的150行程式碼...
        return bookShelf;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;於是當有人要取得&lt;code&gt;BookShelf&lt;/code&gt;物件時，如果直接用&lt;code&gt;new BookShelf()&lt;/code&gt;是行不通的，而且為了達到更好的封裝效果，建構子只有在相同一個&lt;code&gt;package&lt;/code&gt;才能呼叫。 用戶端如果要取得&lt;code&gt;BookShelf&lt;/code&gt;物件，只要使用可以很快速取得的ShelfFactory，就可以取得很裡雜的&lt;code&gt;BookShelf&lt;/code&gt;物件…&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;... 前面只要2行可以取得context物件...
ShelfFactory boFactory = context.getBean(ShelfFactory.class);
BookShelf bookShelf = boFactory.getBookShelf(&amp;quot;POMO&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;眼尖的讀者，可能開始在猜&lt;code&gt;context&lt;/code&gt;物件不會是Spring framework裡面的東西吧？ 沒錯，就是你所想的那種東西。&lt;/p&gt;&lt;p&gt;&lt;em&gt;通常來說Factory的function，都會使用&lt;code&gt;static&lt;/code&gt;的宣告，因為&lt;code&gt;Class&lt;/code&gt;是可以直接取得的&lt;/em&gt;。&lt;/p&gt;&lt;h1 id=&quot;-factory-method-pattern&quot;&gt;很常用的factory method pattern&lt;/h1&gt;&lt;p&gt;前面的hello world用處不太大。 不過還記得我在之前的文章裡介紹，&lt;strong&gt;pattern 就是一再重複的東西&lt;/strong&gt;，其實你時常在用，只是沒注意到。我舉一些例子…&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;Calendar calendar = Calendar.getInstance(); //取得Calendar 物件
Logger logger = LoggerFactory.getLogger(getClass()); //取得logger物件
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); //取得Spring context物件
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這幾個物件，大家應該每天都在用，尤其是logger。&lt;/p&gt;&lt;h1 id=&quot;-spring-&quot;&gt;如果你有用過Spring 那你一定有用過&lt;/h1&gt;&lt;p&gt;還有如果你有使用過Spring famework，你就一定有用過的一個factory method。 還記得上面那個&lt;del&gt;看似&lt;/del&gt;沒用的hello world 裡，利用spring  framework取得&lt;code&gt;ShelfFactory&lt;/code&gt; 物件的部份嗎？ 也是使用simple facotry pattern喔。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;ShelfFactory boFactory = context.getBean(ShelfFactory.class); //其實spring 產生bean的過程是很裡複雜的
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果去trace Spring 的&lt;code&gt;getBean&lt;/code&gt;跑最後，大概是這樣的一段，你說建構這樣一個bean會不會很複雜…&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public &amp;lt;T&amp;gt; T getBean(Class&amp;lt;T&amp;gt; requiredType, Object... args) throws BeansException {
    Assert.notNull(requiredType, &amp;quot;Required type must not be null&amp;quot;);
    String[] beanNames = getBeanNamesForType(requiredType);
    if (beanNames.length &amp;gt; 1) {
        ArrayList&amp;lt;String&amp;gt; autowireCandidates = new ArrayList&amp;lt;String&amp;gt;();
        for (String beanName : beanNames) {
            if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
                autowireCandidates.add(beanName);
            }
        }
        if (autowireCandidates.size() &amp;gt; 0) {
            beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
        }
    }
    if (beanNames.length == 1) {
        return getBean(beanNames[0], requiredType, args);
    }
    else if (beanNames.length &amp;gt; 1) {
        Map&amp;lt;String, Object&amp;gt; candidates = new HashMap&amp;lt;String, Object&amp;gt;();
        for (String beanName : beanNames) {
            candidates.put(beanName, getBean(beanName, requiredType, args));
        }
        String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
        if (primaryCandidate != null) {
            return getBean(primaryCandidate, requiredType, args);
        }
        String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
        if (priorityCandidate != null) {
            return getBean(priorityCandidate, requiredType, args);
        }
        throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
    }
    else if (getParentBeanFactory() != null) {
        return getParentBeanFactory().getBean(requiredType, args);
    }
    else {
        throw new NoSuchBeanDefinitionException(requiredType);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;div style=&quot;font-size:small&quot;&gt;&lt;code&gt;getBean&lt;/code&gt;的宣告是：public &lt;T&gt; T getBean(Class&lt;T&gt; requiredType, Object... args) throws BeansException； 他的權限是purlic； method name 是&lt;code&gt;getBean&lt;/code&gt;，需要傳入requiredType，指定回傳型別，以及一些其他所需要的參數…&lt;/div&gt;&lt;br /&gt;
&lt;h1 id=&quot;-&quot;&gt;什麼是封裝&lt;/h1&gt;&lt;p&gt;小時候在學物件導向時，&lt;del&gt;背&lt;/del&gt;知道物件導向的三大特性：封裝、繼承、多型。 在講解封裝時&lt;del&gt;沒什麼用&lt;/del&gt;的HelloWorld大概是這樣：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public BigDecimal getSalary() {
    return salary;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;那時總沒什麼感覺，總在納悶為什麼這樣子的封裝會引起世界的潮流。 想來是那時真沒什麼實務經驗。 實務上封裝的應用，會是像上頭舉的Spring framework裡的&lt;code&gt;getBean&lt;/code&gt;這樣。有了封裝，我們只要呼叫&lt;code&gt;getBean&lt;/code&gt;這一個method，丟入所需要參數即可，不用花時間去了解這麼複雜的建構過程。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;搭配應用&lt;/h1&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Singleton&lt;br&gt;Factory method通常會搭配Singleton一起應用。 上面的例子其實有搭配了singleton的1/4招─一半私有化(&lt;code&gt;private&lt;/code&gt;)建構子─而且還不是完全私有化，因為使用的是&lt;br&gt;&lt;code&gt;package&lt;/code&gt;層級。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Template method&lt;br&gt;之前的例子，是回傳&lt;code&gt;BookShelf&lt;/code&gt;物件，但我可以回傳&lt;code&gt;Shelf&lt;/code&gt;物件。使用的人可以不用在意，取到的實例是&lt;code&gt;BookShelf&lt;/code&gt;或是&lt;code&gt;PromtionShelf&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h1 id=&quot;-&quot;&gt;結語&lt;/h1&gt;&lt;p&gt;Factory method是很簡單的一種pattern，他也可以算是其他應用的基礎。 如果以武功招式來說，他可以算是起手式。 大家要好好熟悉呀！&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8742/16344695913_8bccfc443e_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div style=&quot;font-size:small&quot;&gt;Factory method 算是一種起手式 &lt;/div&gt;&lt;br /&gt;
&lt;h1 id=&quot;-&quot;&gt;更新&lt;/h1&gt;&lt;p&gt;2015-05-02: 將原名稱為simple factory改為factory method.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/8783427506332552717/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/8783427506332552717' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/8783427506332552717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/8783427506332552717'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/design-pattern-102-simple-factory.html' title='Design Pattern 102: Factory Method'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-5883025322365862977</id><published>2015-03-25T22:40:00.001+08:00</published><updated>2015-05-11T08:51:07.253+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CVS"/><category scheme="http://www.blogger.com/atom/ns#" term="How To"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>如何取得CVS的檔案異動清單</title><content type='html'>&lt;p&gt;CVS 是一個老牌的版本控管軟體，N年前應該真的是很夯，甚至可能是第一個版本控管軟體，不過這幾年幾乎快被其他的東西取代掉了。 雖然覺得是已經快淘汰的東西，不過有工作上有需要用到，還是得研究一下。 今天主要是介紹，如何在CVS上取得需求異動清單。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7647/16734663518_77faf2dd21_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div style=&quot;font-size:small&quot;&gt;Google 上搜尋CVS已經變成藥局了(圖片引用自&lt;a href=&quot;http://www.cvshealth.com&quot;&gt;CVS Health官網&lt;/a&gt; )&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;基本上，以目前所看到的一些GUI工具，似乎沒辦法很順利的提供這樣的功能，所以這個方法主要是採用命令列方式來進行。&lt;a name=&#39;more&#39;&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;簡單來說就是下這樣的指令：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;cvs -d ${CVS_ROOT} log  -d &amp;quot;2015-03-23 00:00:00&amp;lt;2015-03-25 00:00:00&amp;quot;  -SN -w${LOGIN_USER}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;產出結果會是這樣：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8714/16922198435_5dd332f8c7_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;Working file&lt;/code&gt;就是修改的檔案。&lt;em&gt;注意&lt;code&gt;-w&lt;/code&gt;與&lt;code&gt;${LOGIN_USER}&lt;/code&gt;必需連在一起&lt;/em&gt;。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;充滿好奇心的小朋友想知道的事&lt;/h1&gt;&lt;p&gt;你是這樣的人嗎？ 那請繼續看下去…&lt;/p&gt;&lt;h2 id=&quot;-3-&quot;&gt;我剛輸了3小東西？&lt;/h2&gt;&lt;p&gt;這指令的意思是查詢日期區間2015/3/23 ~ 2015/3/25，這段時間區間的log，而且是${LOIGN_USER}提交的東西。 &lt;/p&gt;&lt;h2 id=&quot;-cvs&quot;&gt;所謂的CVS&lt;/h2&gt;&lt;p&gt;就如同SVN一樣，CVS也是一種規格，也有許多版本的實作。 有時候我們在網路上找的一些文件，或許有這樣的功能，但下了指令後，才發現根本不行。這並不見得是文件有錯，只是很可能你所使用的實作版本，並不支援這樣的功能。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;時間格式&lt;/h2&gt;&lt;p&gt;CVS這裡的時間格式是採用 &lt;a href=&quot;http://zh.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO 8601&lt;/a&gt; 而且&lt;em&gt;沒有時區&lt;/em&gt; 。 網路上有文件說明可以下&lt;code&gt;-z&lt;/code&gt;這個參數帶時區，不過在我的執行環境下是找不到這個參數。這很可能是之前講的是基於實作版本的差異。  大家在使用的時候，還是要去參照實作的官方文件。&lt;/p&gt;&lt;p&gt;那什麼是&lt;em&gt;沒有時區&lt;/em&gt;？&lt;/p&gt;&lt;p&gt;稍微岔開講一下&lt;strong&gt;時區&lt;/strong&gt;的故事。 &lt;/p&gt;&lt;p&gt;當你在看這篇文章的這個moment，地球上每個人的手錶上所指的時間都有些許的不一樣。 當然，或許這是因為你的錶和我的錶不是同一支所導致，但更多時候是因為我在台灣你在香港，地點不一樣的關係。 這就是時區。那麼每個人所在地的時區都不一樣，總得有個標準呀；不然當我告訴你：&lt;/p&gt;&lt;p&gt;「我這個檔案已經在2015/3/24 07:25:08 提交到版本控管了，你可以去看一下。」&lt;/p&gt;&lt;p&gt;結果你一看手錶，&lt;/p&gt;&lt;p&gt;「現在才 2015/3/24 06:25:08, 你在騙誰…？」&lt;/p&gt;&lt;p&gt;這樣的情況，那就莫妙奇妙了。 這就是時區造成的影響。 那麼我們要以什麼為標準呢？ &lt;/p&gt;&lt;p&gt;&lt;em&gt;標準就是UTC&lt;/em&gt;。 UTC(Coordinated Universal Time)是世界標準時間的縮寫，指得是英國那一帶的時間。 不過並&lt;strong&gt;不是&lt;/strong&gt;專門指常見的格林威治。當提到&lt;em&gt;格林威治&lt;/em&gt;的時間時，是用&lt;code&gt;GMT&lt;/code&gt;，所以GMT才是指&lt;em&gt;格林威治標準時間&lt;/em&gt;(Greenwich Mean Time，GMT)。 其實GMT也是一個標準，以實際數字上來說，2個是差不多；差異的地方除了一些歷史的因素外，還有一些天文與科學的原因，但是我們還是先到此打住吧！ 我們還有其他的東西需要研究。&lt;/p&gt;&lt;p&gt;好，講完標準了，現在來講時區。 &lt;/p&gt;&lt;p&gt;假設我現在人在台灣，如果英國那一帶(&lt;em&gt;不是格林威治喔&lt;/em&gt;)的時間是2015/3/24 7:25 那麼台北的時間你可以記作&lt;code&gt;2015-03-24T15:25+08:00&lt;/code&gt; 。沒錯，我現在手錶的時間是其實是下午3點多。我沒那麼勤奮，台灣時間早上7點25分提交程式。 雖然之前的範例區間是跨了48小時，但是其實我想查的是「台灣時間 2015/3/24 下午3點左右時，由我所提交的日誌」記錄，然後藉由查詢日誌，取得異動清單。 &lt;/p&gt;&lt;p&gt;這樣當你了解CVS的日期格式之後，你可以正確的輸入他的時間區間了。 之前提到CVS log的時間&lt;em&gt; 沒有時區&lt;/em&gt; 的概含，因為他的時間是UTC─世界標準時間。 所以當你在查詢的時候，也必需要自己換回UTC的時區。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;cvs -d ${CVS_ROOT} log   -d &amp;quot;2015-03-24 07:00:00&amp;lt;2015-03-24 08:00:00&amp;quot;  -SN -w%{LOGIN_USER}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;-d-&quot;&gt;參數&lt;code&gt;-d&lt;/code&gt;的另外用法&lt;/h2&gt;&lt;p&gt;&lt;code&gt;-d&lt;/code&gt;是指定時間區間。 也有其他的用法：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;-d &amp;quot;10 min ago&amp;quot;  # 10分鐘以前
-d &amp;quot;10 min&amp;quot;      # 10分鐘以內
-d &amp;quot;2015-03-24&amp;lt;&amp;quot; # 超過 2015/3/24
-d &amp;quot;&amp;lt;2005-10-01&amp;quot; # 2015/3/24 以前
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;-&quot;&gt;另存新檔&lt;/h2&gt;&lt;p&gt;你可以將這些產出結果另存檔案&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;${剛才輸入的一連串指令} &amp;gt; chage_list.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;是將輸出結果轉向。 Linux上很常用到這樣的指令。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;結論&lt;/h1&gt;&lt;p&gt;詳細的參數，可以參考線上文件，或是直接下&lt;code&gt;--help&lt;/code&gt;看參數的用法，只不過基於這重還不知道有沒有人在維護的東西，可能有時候，這文件也不見得會準，還是要實際使用看看才會知道。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/5883025322365862977/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/5883025322365862977' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/5883025322365862977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/5883025322365862977'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/cvs.html' title='如何取得CVS的檔案異動清單'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-8626140887931332876</id><published>2015-03-24T01:40:00.001+08:00</published><updated>2015-04-02T10:59:50.531+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design Pattern"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Design Pattern 101</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8659/16600435959_49ba8fc4ef_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div align=&quot;center&quot; style=&quot;font-size:small&quot;&gt;這是strategy pattern &lt;/div&gt;&lt;br /&gt;
&lt;h1 id=&quot;-&quot;&gt;引言&lt;/h1&gt;&lt;p&gt;第一次寫design pattern 的文章。 這一系列的文章也是想寫很久了，今天總算是開宗第一篇…。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-pattern&quot;&gt;什麼是pattern&lt;/h1&gt;&lt;p&gt;Pattern ，中譯名叫「樣式」，就是每次在這樣的情況下，大家都會那樣做的一種行為模式。&lt;/p&gt;&lt;p&gt;比方說，在電影情節裡，只要有公主，就一定會有魔王； 有魔王就一定會閒閒沒事跑去抓公主或到處去姦淫擄掠。如果公主被抓了，就一定會蹦出一個英雄，然後正義凜然的會跑去救他。這就是pattern。 又比方說，我最近在講給小朋友聽的一些故事裡，不管是白雪公主還是長髮公主，也不管這些女孩子們經歷了多少苦難，但最後一定會和王子過著幸福快樂的日子。(其實我不是很確定白雪公主跟長髮公主的老公，是不是相同一個王子；小朋友不懂問，我也不敢提 &lt;img src=&quot;http://farm8.staticflickr.com/7651/16600384969_2614917f0f_o.png&quot; alt=&quot;&quot;&gt; )。也不管是三隻小豬還是七隻小羊，甚至是小紅帽，基本上只要你是大野狼，最後一定得死。 這也是pattern。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7601/16280481634_afe55bb349_o.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div align=&quot;center&quot; style=&quot;font-size:small&quot;&gt;其實我不是很確定這些公主們的老公，是不是相同一個王子&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;這些童話故事可能離我們稍微遠了一點。沒關係，我舉些生活周遭的例子。&lt;/p&gt;&lt;p&gt;比方說一些文章標題的命名方式：「有錢人不告訴你的十件事」、「券商不告訴你的九件事」、「股市高手不敢說的事」； 這是pattern。 我們日常使用的應用軟體，不管是wrod、Excel、Chrome Browser，最上一排是功能列，接下來可能會有一些工具列，接下來才會是主要的工作區域；這是使用者界面設計的pattern。 也有不少廣告，不論介紹什麼產品，只要經費足夠，就會去找一個公眾人物來代言，不管這個人物有沒有使用過這個產品；這也是pattern。&lt;/p&gt;&lt;p&gt;Pattern 總的來說，就是大家一直都在做的事。 雖然是大家都在做的事，可是通常沒講你不會留意到。 就如同許多標題殺手：「XXX都在做的事」一樣，不講覺得很神祕，可是講了，你也真的了解了，就會說：「這我早知道了」。 pattern就是這樣的東西。 &lt;/p&gt;&lt;h1 id=&quot;-design-pattern-&quot;&gt;什麼是design pattern？&lt;/h1&gt;&lt;p&gt;這裡提到的 Design pattern 也是一種pattern，只是著重在於軟體系統方面。這是是許許多多的高手，在經歷過許許多多的考驗，整理出幾種常用的程式設計方式，尤其著重於如何配合與使用物件。&lt;/p&gt;&lt;h1 id=&quot;design-pattern-&quot;&gt;Design pattern的歷史&lt;/h1&gt;&lt;p&gt;Design Pattern 一開始並不是出現在軟工界，而是起源於建築業。 有名的書是由Erich Gamma 等4人所著的&lt;a href=&quot;http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented-ebook/dp/B000SEIBB8&quot;&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/a&gt;，俗稱4人幫，簡稱GoF。 裡面一共有23種Pattern、分為3大類。 不過時事變遷，已經不再只有這樣了，而Design Pattern的應用也不再那麼單純。 有些pattern 可以小小的幾行程式碼或是3~5個類別就可以呈現出來，有些pattern 則是一個framework的精髓所在。Pattern 除了在OO、class 的設計上使用，也導到系統架構上。除了pattern，也有所謂的AntiPattern出現；白話的說法就是：「許多人會犯的錯」。 還記得前面所講的，pattern其實是「大家都在做的事」嗎？ 這「事」也包含了「&lt;strong&gt;錯&lt;/strong&gt;的事」。&lt;/p&gt;&lt;h1 id=&quot;design-pattern-&quot;&gt;Design pattern有那些&lt;/h1&gt;&lt;p&gt;GoF所提出來的23種 design pattern列舉如下：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;創建型模式&lt;ul&gt;&lt;li&gt;Abstract Factory Pattern&lt;/li&gt;
&lt;li&gt;Simple Factory Pattern&lt;/li&gt;
&lt;li&gt;Builder Pattern&lt;/li&gt;
&lt;li&gt;Prototype Pattern&lt;/li&gt;
&lt;li&gt;Singleton Pattern&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;結構型模式&lt;ul&gt;&lt;li&gt;Adapt Pattern&lt;/li&gt;
&lt;li&gt;Bridge Pattern&lt;/li&gt;
&lt;li&gt;Composite Pattern&lt;/li&gt;
&lt;li&gt;Decorate Pattern&lt;/li&gt;
&lt;li&gt;Facade Pattern&lt;/li&gt;
&lt;li&gt;Flyweight Pattern&lt;/li&gt;
&lt;li&gt;Proxy Pattern&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;行為型模式&lt;ul&gt;&lt;li&gt;Chain of Reponsibility Pattern&lt;/li&gt;
&lt;li&gt;Command Pattern&lt;/li&gt;
&lt;li&gt;Interpreter Pattern&lt;/li&gt;
&lt;li&gt;Iterator Pattern&lt;/li&gt;
&lt;li&gt;Mediator Pattern&lt;/li&gt;
&lt;li&gt;Memento Pattern&lt;/li&gt;
&lt;li&gt;Observer Pattern&lt;/li&gt;
&lt;li&gt;State Pattern&lt;/li&gt;
&lt;li&gt;Strategy Pattern&lt;/li&gt;
&lt;li&gt;Template method Pattern&lt;/li&gt;
&lt;li&gt;Visitor Pattern&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h1 id=&quot;-design-pattern-&quot;&gt;為什麼要學Design pattern？&lt;/h1&gt;&lt;p&gt;有&lt;a href=&quot;http://zonble.net/archives/2014_04/1607.php&quot;&gt;網友&lt;/a&gt;解釋，Design pattern 像格鬥電玩裡的連續技，我覺得很貼切。 一開始學會了程式的宣告、語法以及hello world，就像會用搖杆移動角色，看起來沒什麼。當你會學會使用函式庫，就好像學會了一些招式，開始有點威力。 不過當你了解到可以將某些招式組合起來成為連續技，並在不同時機點上使用，就可以對對手造成不小的傷害，這就是design pattern。&lt;/p&gt;&lt;p&gt;基本上pattern可以說是高手們的心得與結晶、他們歸納出來的武功秘笈。高手們都這樣做，一般庸手可以不這樣做嗎？ &lt;img src=&quot;http://farm8.staticflickr.com/7651/16600384969_2614917f0f_o.png&quot; alt=&quot;&quot;&gt; 好吧，這個理由對我來說是有牽強了點。我一向主張&lt;em&gt;不盲目的依樣畫葫蘆&lt;/em&gt;。&lt;/p&gt;&lt;p&gt;其實學會design patten 可以讓你可以快速的看懂別人的程式碼，自己撰寫出別人可以看得懂的程式碼； 不再維護系統後不怕被人追殺。 但是在剛學的時候需要注意，不要為了使用pattern而用pattern，這樣很容易走火入魔。 這就好像連續技用錯時機點，也往往很容易被對手KO。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;需要先會什麼&lt;/h1&gt;&lt;h2 id=&quot;uml-&quot;&gt;UML。&lt;/h2&gt;&lt;p&gt;大多數的design pattern教學都是使用UML來做為描述。 UML 不需要全部都了解，只要知道sequence diagram與class diagram 兩張圖就可以了。也不見得需要會畫，但是需要看得懂。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;一種程式語言&lt;/h2&gt;&lt;p&gt;我建議Java。 建議Java的理由不是因為他特別好、特別帥，而是因為我比較熟。 基本上，只要是屬於OO導向的，其實任何一種語言都可以。 沒錯，&lt;strong&gt;design pattern 無關乎語言種類&lt;/strong&gt;。 &lt;/p&gt;&lt;p&gt;話說回來，所謂的物件導向，也是「沒講你不會留意到」的東西呀！&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/8626140887931332876/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/8626140887931332876' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/8626140887931332876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/8626140887931332876'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/design-pattern-101_24.html' title='Design Pattern 101'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-4769367936386997354</id><published>2015-03-18T21:49:00.000+08:00</published><updated>2015-04-02T11:29:33.076+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SVN"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>SVN 102：SVN使用上的一些小技巧</title><content type='html'>&lt;h1 id=&quot;-&quot;&gt;前言&lt;/h1&gt;&lt;p&gt;前一陣子寫了&lt;a href=&quot;http://taichitech.blogspot.tw/2015/03/svn.html&quot;&gt;一篇文章&lt;/a&gt;，有提到我平常如何使用SVN；今天這篇文章繼續這個主題...&lt;a name=&#39;more&#39;&gt;&lt;/a&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;code&gt;右鍵&lt;/code&gt;&amp;gt;&lt;code&gt;Tortoise SVN&lt;/code&gt;&amp;gt;&lt;code&gt;Setting&lt;/code&gt;&amp;gt;&lt;code&gt;General&lt;/code&gt;&amp;gt;&lt;code&gt;Context Menu&lt;/code&gt;開啟設定視窗，將常用的功能放到右鍵選單第一層。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7613/16660516510_c42202b22a_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8699/16847831652_6a81bbec94_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div style=&quot;font-size:small&quot; align =&quot;center&quot;&gt;因為我時常會檢視log，所以放了&lt;code&gt;show log&lt;/code&gt;在第一層。這樣我就不用滑鼠移動2次，可以減少一些時間&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;-&quot;&gt;修改忽略名單&lt;/h2&gt;&lt;p&gt;在我電腦上面，或是工作環境下，有些東西不應該上版本控管。 在這種情形下，我就會在&lt;em&gt;全域環境設定&lt;/em&gt;下，將那些檔案，設為忽略名單。 設置全域忽略名單在&lt;code&gt;右鍵&lt;/code&gt;&amp;gt;&lt;code&gt;Setting&lt;/code&gt;&amp;gt;&lt;code&gt;General&lt;/code&gt;。 設置忽略名單，可以讓你不會不小時將不該進行版本控管的檔案進行版本控管。&lt;/p&gt;&lt;p&gt;這裡的設置是用&lt;strong&gt;空白&lt;/strong&gt;做分隔，可以搭配萬用字元(&lt;code&gt;*&lt;/code&gt;)。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8690/16846716352_6507499045_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;那麼什麼東西應該&lt;strong&gt;通常&lt;/strong&gt;被忽略呢？ 我舉一些例子：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;暫存檔。例如&lt;code&gt;~*&lt;/code&gt;、&lt;code&gt;.*&lt;/code&gt;(以流水號為開頭，或是沒有檔名只有副檔名的檔案)。 &lt;/li&gt;
&lt;li&gt;執行檔。例如&lt;code&gt;*.class&lt;/code&gt;(Java的執行檔)&lt;/li&gt;
&lt;li&gt;日誌。例如&lt;code&gt;*.log&lt;/code&gt;。 &lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;以下是我本機的設定，不過&lt;em&gt;只是參考而已&lt;/em&gt;，各位還是需要依自己的需求來進行設定。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;*.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo __pycache__ *.rej *~ #*# .#* .*.swp .DS_Store .* ~* *.class classes *.log ~* .*
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;-&quot;&gt;變更檔名&lt;/h2&gt;&lt;p&gt;你可能會覺得奇怪，變更檔名不是直接使用F2變更就好了，為什麼還要特定用Tortoise 來變更呢？ 主要是因為log的關係。 如果只是單純的使用Windows 作業系統的F2變更檔名，在SVN的log中只會呈現：少了一個檔案與多了一個檔案，這樣的資訊； 不過如果使用Tortoise SVN來變更檔名的話，在log會顯示原來的檔名, 就以知道「原來這個檔案是由另外一個檔案變更過來的」。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8572/16662372318_7450606172_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div style=&quot;font-size:small&quot;&gt;我們可以知道DynaViewResult.java是由DynaViewResultSet.java變更而來的&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;-&quot;&gt;移動目錄或檔案&lt;/h2&gt;&lt;p&gt;方法很簡單。 用右鍵進行拖曳的動作，放開滑鼠後，會出現快速選單。選擇&lt;code&gt;Move versioned item(s) here&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7618/16660618670_5c3fc3f8bf_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;SVN系列是Tortoise 專屬的功能，當然，你也可以選擇移動順便改檔名，或是複製。 當然，移動的目的地是需要已經進入版本控管。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;復原到特定版本&lt;/h2&gt;&lt;p&gt;如果直接點右鍵的update，是會將目前工作區域直接update到server最新版本。 點選 &lt;code&gt;右鍵&lt;/code&gt;&amp;gt;&lt;code&gt;Tortoise SVN&lt;/code&gt;&amp;gt;&lt;code&gt;Update to revision&lt;/code&gt;可以將工作區域回到特定版本。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;比較檔案&lt;/h2&gt;&lt;p&gt;如果一個檔案在本機上有了修改，這時點選&lt;code&gt;右鍵&lt;/code&gt;&amp;gt;&lt;code&gt;Tortoise SVN&lt;/code&gt;，會有&lt;code&gt;Diff&lt;/code&gt;這個選項，可以比較工作域的檔案與server目前的檔案的差異。 也可以選擇&lt;code&gt;Diff with previous version&lt;/code&gt;比較server最近版本與上一版的差異。&lt;/p&gt;&lt;h2 id=&quot;log-&quot;&gt;Log視窗的妙用&lt;/h2&gt;&lt;p&gt;呼叫出log視窗後，在log上點選右鍵，有許多功能，是針對該版本檔案的操作。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7288/16662172460_8c69c42240_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;以下幾個是我常用的功能：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;Cmpare with working copy&lt;/code&gt;。可以比對該版本與本機檔案的差別。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Compare with previous revision&lt;/code&gt; 比對該版本與前一版本的差異。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Create branch/tag from revision&lt;/code&gt;直接從這個版本建立。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Merge Revision to&lt;/code&gt; 將某個branch merge到這個版本。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Export&lt;/code&gt; 匯出這個版本的檔案。 匯出的檔案是不包含&lt;code&gt;.SVN&lt;/code&gt;控制目錄。&lt;/li&gt;
&lt;/ol&gt;&lt;h2 id=&quot;-svn-server-&quot;&gt;檢視SVN server上的檔案&lt;/h2&gt;&lt;p&gt;你可以點選&lt;code&gt;repo-browser&lt;/code&gt; 來檢視目前server上資料或目錄架構。&lt;/p&gt;&lt;h2 id=&quot;switch&quot;&gt;Switch&lt;/h2&gt;&lt;p&gt;這個功能在之前的文章裡有提過，是切換branch用的。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;其實還有許多功能&lt;/h1&gt;&lt;p&gt;Tortoise 的功能很多，不過不管你在本機上如何操作都不用擔心，只要沒有commit，隨時都可以重新在update或是revert回去，大家可以放心的試試。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/4769367936386997354/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/4769367936386997354' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4769367936386997354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4769367936386997354'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/svn-102svn.html' title='SVN 102：SVN使用上的一些小技巧'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-7405465348817822278</id><published>2015-03-17T10:53:00.001+08:00</published><updated>2015-03-17T11:05:43.728+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SVN"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>我平常如何使用SVN？</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8576/16796321241_60a1ececd4_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;身為一個軟體開發者，你一定要知道如何使用SVN。 我稍微介紹一下，我平日如何使用Tortoise SVN…&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-svn&quot;&gt;什麼是SVN&lt;/h1&gt;&lt;p&gt;Subversion是一個開放原始碼的版本控管系統，簡稱SVN。 目前由Apache維護。 基本上SVN作者是打算令SVN可以取代CVS(也是另一套版本控管系統)，所以在許多地方SVN與CVS是很相像的。 你可以直接在令命列模式下，下指令操作SVN，也有一些現成的GUI(Graphical User Interface，使用者圖形介面)來操作。 比較有名的是 TortoiseSVN(本篇重點)；搭配Eclipse Plugin，你可以服用 Subclise。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://subclipse.tigris.org/&quot;&gt;Tortoise SVN&lt;/a&gt; &lt;br /&gt;
&lt;a href=&quot;http://subclipse.tigris.org/&quot;&gt;Subclipse&lt;/a&gt;&lt;/p&gt;&lt;p&gt;SVN除了可以當檔案改壞時，將檔案回復到之前的樣子，也可以對相同產品、不同版本間的程式碼，進行管理。 &lt;/p&gt;&lt;p&gt;&lt;em&gt;不過如果你只需要進行不同幾器間檔案的同步的話，SVN並不是一個建議的選項&lt;/em&gt;。 我會建議使用其他的檔案同步軟體，例如&lt;a href=&quot;http://www.freefilesync.org/&quot;&gt;FreeFileSync&lt;/a&gt;，或是&lt;a href=&quot;http://www.allwaysync.com/&quot;&gt;AllaySync&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;那麼一天下來，我如何使用SVN？&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;每天第一件事就是更新&lt;/h1&gt;&lt;p&gt;每天早上第一件是就是進行update的動作。 先更新別人的檔案，然後才可以在別人已經修改過後的檔案上再增加自己想要修改的部份；不然merge的動作，其實是很累人的。 如果是小檔，或者別人改的幅度不太大的話就還好，但如果是在5000行中的檔案，加上了200行的程式碼，要找出這200行可就累人了。&lt;/p&gt;&lt;h1 id=&quot;-lock-&quot;&gt;搭配lock 機制，檔案大家輪流改&lt;/h1&gt;&lt;p&gt;版本控管理論裡，解決沖突的方式有&lt;strong&gt;Copy-Modify-Merge&lt;/strong&gt; 與 &lt;strong&gt;Lock-Modify-Unlock&lt;/strong&gt; 2種方式。Copy-Modify-Merge就是先取得檔案來修改，如果有沖突的再進行merge。 Loic-Modify-Unlock 是先鎖定要改的檔案(鎖定後其他人都不能再進行修改)，進行修改；等一個人改完這個檔案後，再輪到下一個人改。 原則上我會採用第2種機制，做為團隊共同編輯的解決方案。 &lt;del&gt;基於小團隊， 偶爾會偷雞採用一下第一種機制&lt;/del&gt;。&lt;/p&gt;&lt;p&gt;使用Lock-Modify-Unlock的 產程如下：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;em&gt;不論要做什麼修改，都需要進行鎖定的動作&lt;/em&gt;。於是第一個要改檔案的人可以先鎖定檔案一個沒有被銷定的檔案來進行修改。&lt;/li&gt;
&lt;li&gt;修改完後，將&lt;em&gt;檔案儲存起來，並解除鎖定&lt;/em&gt;，讓檔案可以給下一個人進行更改。於是下一個人就又可以進行更改了。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7654/16802899112_3d06438156_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div style=&quot;font-size:small&quot; &gt;Harry先Lock了檔案，並將檔案由A改為A`。 Sally等到Hrry unlock後才可以再lock檔案。 圖檔取自SVN 文件&lt;/div&gt;&lt;br /&gt;
&lt;h1 id=&quot;-branch&quot;&gt;依照產品維運需求，使用branch&lt;/h1&gt;&lt;p&gt;我平常的工作，是針對一套已經上線的產品進行維護。 基於這樣的需求，會使用大量的branch。 那麼我是如何設計我工作環境的branch呢？ &lt;/p&gt;&lt;h2 id=&quot;-branch-&quot;&gt;一開始建立的branch。&lt;/h2&gt;&lt;p&gt;一開始，我會有3個branch：&lt;strong&gt;master&lt;/strong&gt;、 &lt;strong&gt;developer&lt;/strong&gt;、&lt;strong&gt;features&lt;/strong&gt;。 &lt;em&gt;Master是目前已上線的版本，developer 算是beta版，features則是針對每個需求異動所衍生出來的版本集合&lt;/em&gt;。 Features底下會有更多的branch，原則上每個branch會對應到一個需求異動。我習慣使用日期編號來當作每個需求異動的版本；當然這並不是一個絕對的作法。另外一種建議是使用有義意的名稱，而首當其衝的第一個問題就是SVN對中文branch名稱的支援程度。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7610/16821653765_ff530bfbda_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div align =&quot;center&quot; style=&quot;font-size:small&quot;&gt;實務上建立的branch&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;你可能會想知道如何建立Branch。這點Google上已經有不少範例， 有空我也會再多做些補充。&lt;/p&gt;&lt;h2 id=&quot;switch&quot;&gt;Switch&lt;/h2&gt;&lt;p&gt;建立完branch後，會將work區域 switch到該branch，然後進行修改。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7653/16838366451_f82e143360_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;-commit&quot;&gt;修改 &amp;amp; Commit&lt;/h2&gt;&lt;p&gt;接下來，就是依照團隊合作的方式，是否採用lock機制，然後按照需求進行檔案的修改，最後將修改的檔案commit回SVN server。&lt;/p&gt;&lt;h2 id=&quot;-merge&quot;&gt;重頭戲：Merge&lt;/h2&gt;&lt;p&gt;依照上線的狀況會再將branch的程式碼，merge回develop。 等到上線一陣子，上線也都沒問題了，就將這需求所異動的程式碼再merge 到master。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;如何取得異動清單&lt;/h1&gt;&lt;p&gt;有時候，我必需針對這一個異動需求，取得相關的異動清單。 不過一個需求異動，可能前前後後改了好幾次，也commit了好幾次。 Tortoise提供了很快速的方法，可以一口氣取得多次commit的異動清單：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;針對work點選右鍵。&lt;/li&gt;
&lt;li&gt;選擇&lt;code&gt;show log&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;選擇該需求相關commit的紀錄。&lt;/li&gt;
&lt;li&gt;在log 上點選右鍵，選擇&lt;code&gt;compare revision&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8656/16652380020_55cac5bd6d_o.png&quot; alt=&quot;&quot;&gt;&lt;br /&gt;
&lt;img src=&quot;http://farm9.staticflickr.com/8712/16652035978_9c2a681024_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;由於是使用版本比較的功能，所以必需再&lt;strong&gt;多選上一個變動前的版本&lt;/strong&gt;，這樣才可以進行比較。 比方說我上一個需求，最後一個版本是320；這個需求commit了2個版本(321、323)，所以在&lt;code&gt;compare revision&lt;/code&gt;時需要選擇包含320這個版本(320、321、323)。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;偶爾有需要，使用暫存的儲存庫&lt;/h1&gt;&lt;p&gt;由於工作場所的限制，某些協同工作並沒有版本控管的機制。 為了確保在修改檔案的可回復性，所以我會在自己的本機建立一個SVN repository。 建立SVN repository的方法很簡單，使用Totoise SVN，找一個空的資料夾，點右鍵，選擇create repository就可以建立了。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7635/16651953390_abbc0cf8fb_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;建立完儲存庫後，我會將每個要改動的檔案，移到&lt;em&gt;另一個資料夾&lt;/em&gt;，並提交(commit)到儲存庫上去。 這樣我即使改壞，也有一個備份機制。即使多次基於不同的需求，異動相同的檔案，也可以隨時更新回去到上一個版本，或是與更早之前的版本進行比對。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;最後&lt;/h1&gt;&lt;p&gt;版本控管是我日常工作中很重要的一個工具。 我覺得SVN最大的特色，並不是在update回上一個版本，或是檔案備份，而是&lt;em&gt;程式碼branch間的控管&lt;/em&gt; 與 &lt;em&gt;使用lock-unlock，來進行協同工作間的檔案同步&lt;/em&gt;。 不過戲法人人會變，巧妙各有不同； 這裡拋磚引玉地提出一些我平常使用的方式。如果大家有其他的用法，也可提出來分享或討論。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;延申閱讀&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;http://taichitech.blogspot.tw/2007/05/subversion-on-windows.html&quot;&gt;Subversion on windows安裝&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/7405465348817822278/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/7405465348817822278' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7405465348817822278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7405465348817822278'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/svn.html' title='我平常如何使用SVN？'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-4217182906882251538</id><published>2015-03-13T04:17:00.000+08:00</published><updated>2015-03-17T11:07:31.840+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="How To"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>如何使用Notepad++ 執行Java</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8714/16609053168_e933e05aa3_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;最近剛好需要使用Notepad++ 來執行一些簡單的動作…。 &lt;/p&gt;&lt;p&gt;來看教學吧。簡單的說，&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;簡單的說&lt;/h1&gt;&lt;h2 id=&quot;1-plugin&quot;&gt;1. 下載plugin&lt;/h2&gt;&lt;p&gt;使用Plugin Manager下載NppExec這個Plugin&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7626/16605063560_10cb0f76eb_o.png&quot; alt=&quot;&quot;&gt;&lt;br /&gt;
&lt;img src=&quot;http://farm8.staticflickr.com/7650/16791295051_a7525099c7_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;因為我的環境已經裝過了，所以NppExec在Installed這個分頁裡。 沒裝過的朋友應該可以在available分頁中找到。&lt;/p&gt;&lt;p&gt;安裝好之後，外掛模組這個選單 應該已經多了一個NppExec這個選項。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8629/16605087990_12a7e13c58_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;2-script&quot;&gt;2. 撰寫Script&lt;/h2&gt;&lt;p&gt;安裝好之後，點選&lt;code&gt;外掛模組&amp;gt;NppExec&amp;gt;Execute&lt;/code&gt;，或是&lt;code&gt;F6&lt;/code&gt;熱鍵，叫出出script編輯畫面，輸入script。 輸入完script後儲存成Java… 或是任何你想要的名字。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;javac $(FILE_NAME)
java $(NAME_PART)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;http://farm8.staticflickr.com/7592/16766489366_590f84d914_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;3-&quot;&gt;3. 執行&lt;/h2&gt;&lt;p&gt;撰寫好程式碼後，執行&lt;code&gt;外掛模組&amp;gt;NppExec&amp;gt;Execute&lt;/code&gt;，或按&lt;code&gt;F6&lt;/code&gt;執行程式碼。 你也可以按&lt;code&gt;Ctrl+F6&lt;/code&gt;重複執行上一次執行的script。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8578/16766785166_ee6d955498_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;複雜的說&lt;/h1&gt;&lt;h2 id=&quot;-notepad-java&quot;&gt;為什麼要使用Notepad++ 來執行Java&lt;/h2&gt;&lt;p&gt;以現在的科技來說，軟體開發幾乎是脫離不了IDE；那什麼時候會使用像Notepad ++這種簡單的文字編輯器來執行Java呢？&lt;/p&gt;&lt;p&gt;其實當初會想找這個解決方案，並不是專門為了Java，而是為了需要學習Perl。 沒錯，所以相同的方法，其實也是可以用在其他語言上。 透過這樣簡單的工具設定，我可以快速的測試、練習我所撰寫的程式碼，而不需要啟動繁雜、厚重的IDE。 你知道的，啟動個IDE，致少需要花個30秒，而啟動NotePad ++ 只需要1秒 。 當然，IDE有他的獨到之處，NotePad++是無法完全取代。 在Java中如果需要撰寫更複雜的程式，還是需要IDE的幫忙。&lt;/p&gt;&lt;p&gt;當然如果多點想像力，你可以直接在裡面撰寫更複雜的script，完成一連串的動作。&lt;/p&gt;&lt;h2 id=&quot;console-&quot;&gt;Console 被我不小關掉了怎麼辦？&lt;/h2&gt;&lt;p&gt;請點選 &lt;code&gt;外掛模組&amp;gt;NppExec&amp;gt;Show Console Dialog&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;其他的環境變數&lt;/h2&gt;&lt;p&gt;除了`$(FILE_NAME)與$(NAME_PART)外，還有其他的變數，在Script中編輯的時候，也有AutoComplete功能，會自動帶出可使用的變數。 以下是說明文件中所截錄的一些變數。&lt;/p&gt;&lt;table&gt;&lt;thead&gt;
&lt;tr&gt; &lt;th&gt;變數&lt;/th&gt; &lt;th&gt;範例&lt;/th&gt; &lt;/tr&gt;
&lt;/thead&gt; &lt;tbody&gt;
&lt;tr&gt; &lt;td&gt;$(FULL_CURRENT_PATH)&lt;/td&gt; &lt;td&gt;E:\my Web\main\welcome.html &lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(CURRENT_DIRECTORY)&lt;/td&gt; &lt;td&gt;E:\my Web\main&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(FILE_NAME)&lt;/td&gt; &lt;td&gt;welcome.html &lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(NAME_PART)&lt;/td&gt; &lt;td&gt;welcome &lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(EXT_PART)&lt;/td&gt; &lt;td&gt;.html &lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(NPP_DIRECTORY)&lt;/td&gt; &lt;td&gt;the full path of notepad++&amp;#39;s directory &lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(CURRENT_WORD)&lt;/td&gt; &lt;td&gt;word(s) you selected in Notepad++ &lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(CURRENT_LINE)&lt;/td&gt; &lt;td&gt;current line number&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;$(CURRENT_COLUMN)&lt;/td&gt; &lt;td&gt;current column number&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt; &lt;/table&gt;&lt;br /&gt;
&lt;h2 id=&quot;-&quot;&gt;最後&lt;/h2&gt;&lt;p&gt;在&lt;code&gt;NppExec&amp;gt;Help/Doc&lt;/code&gt;或&lt;code&gt;NppExec&amp;gt;Help/Manual&lt;/code&gt;有說明文件可以參考。 有興趣的讀者可以研究看看。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/4217182906882251538/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/4217182906882251538' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4217182906882251538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4217182906882251538'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/notepad-java.html' title='如何使用Notepad++ 執行Java'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-4593854775571520431</id><published>2015-03-11T02:40:00.000+08:00</published><updated>2015-03-12T16:59:32.197+08:00</updated><title type='text'>好的日誌配置讓你上天堂，不好日誌配置會讓你抓狂：我如何使用日誌配置</title><content type='html'>&lt;p&gt;在開發軟體的過程中，時常需要在畫面上檢視一個變數值。 最值接的做法，就是使用&lt;code&gt;System.out.print(value);&lt;/code&gt; 。 使用print的方式，輸出的標的，預設是標準輸出介面，也就是螢幕。 可如果你想知道正在上線中的產品， 程式碼中的變數值，這樣的做法就很不恰當了。 因為你不會隨時隨地的待在主機前面，盯著螢幕，看著畫面顯示變數值；更甚至，有時因為資料量太多，畫面就這樣一閃而過，根本看不到。 這時如果有一個機制，可以將標準輸出存到檔案去，就很理想了。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;正文開始&lt;/h1&gt;&lt;p&gt;講了那麼多，除了是練練打字、充充篇幅、騙騙稿費外，簡單一句話，就是「介紹logger的機制」。&lt;/p&gt;&lt;p&gt;我在大多數的案子中，使用的是Log4J這個framework~ ，或者說是機制。 不過近年改用Slf4j。 &lt;/p&gt;&lt;p&gt;Log4j是一個老牌的logger 機制，1999年就出來了，正式發行第1版則是到了2001年的事。 不過一直到2006、2007 就沒什麼變動。 2012年才又出了第2版。 這也是我為什麼好好用的log4j，會再去接觸slf4j的原因。 因為這幾年(2007~2012)幾乎是沒什麼更新。 &lt;/p&gt;&lt;h1 id=&quot;-logger&quot;&gt;為什麼要使用logger&lt;/h1&gt;&lt;p&gt;一開始在學習程式設計時，不知道 logger 與System.out.pring的分別，當實際有了專案經驗後，才發覺這真是無與倫比的重要。 &lt;/p&gt;&lt;ul&gt;&lt;li&gt;第一個理由，其實在前段的廢文有提到(那這樣是不是可以就不算廢文？XD)：必需要將訊息保存下來。 螢幕會一閃而過，留在檔案中才得以保存下來。&lt;/li&gt;
&lt;li&gt;將不同的訊息在不同的地方呈現。 一般來說，串流預設的輸出是螢幕。不過如果所有的訊息都一起在螢幕上呈現的話，實在是太雜亂無章，不容易追蹤。 而且當遇到執行方式沒有畫面可以輸出的時候，系統就不知道要輸出到那裡去了。 另外在J2EE環境下，Container(即一般所謂的Tomcat或是JBoss等AP Server) 通常會將該是螢幕輸出的串流，存到自己的log檔。 如果container中有多個AP，則所有AP的log都會顯示在同一份log檔中。 在這種情況下，想要&lt;em&gt;僅僅&lt;/em&gt;檢視自己負責的程式所產生的log資訊並不容易，而且還有資安的疑慮。&lt;/li&gt;
&lt;li&gt;如果在開發時其大量使用System.out.pring顯示變數資訊或其他訊息，要麻是在開發完成前夕，搜尋所有的System.out.print，然後刪除，要麻就是直接放在那裡， 任由訊息輸出，降低系統效率。這時如果在砍掉System.out.print的時候，萬一不小心砍錯，導致系統錯誤，就得不嘗失了。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;基本上，如果使用log framework，以上的問題，都可以提供良好的解決方案。&lt;/p&gt;&lt;h1 id=&quot;logger-&quot;&gt;Logger 組成原素&lt;/h1&gt;&lt;p&gt;所謂的組成原素決對不是風、火、地、水，或是金、木、水、火、土 XD。&lt;/p&gt;&lt;h2 id=&quot;logger&quot;&gt;Logger&lt;/h2&gt;&lt;p&gt;基本上 就是logger的實體。通常來說，如果你不是使用System.out.print的話，一般都會是用&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;Logger this islogger = Logger.getLogger(getClass().getName());
thisislogger.info(&amp;quot;This is message~&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;變數&lt;strong&gt;thisislogger&lt;/strong&gt; 就是一個logger。 通常logger有自己名稱(指得不是變數名稱而是&lt;code&gt;getClass().getName()&lt;/code&gt;)。 Log4j的建議與一般慣例是使用package的名稱。 這可以讓logger有良好的繼承機制。當然，如果你想直接使用像&amp;quot;AP&amp;quot;這樣的名稱也是可以，不過這樣在管理上可能就不是那麼容易。&lt;/p&gt;&lt;h2 id=&quot;appender&quot;&gt;Appender&lt;/h2&gt;&lt;p&gt;簡單來說，就是log訊息的輸出地。(其實我也不知道它為什麼要叫「appender」；叫「output」這樣的名字，不是比較直覺…)。以log4j來說，他至少提供了可以將log訊息導到consol上或是實體檔案上的功能。 Slf4j這個log framework 甚至提供可以將log訊息存到DB的功能。 (log4j不確定是否也提供相同的功能)。&lt;/p&gt;&lt;h2 id=&quot;level&quot;&gt;Level&lt;/h2&gt;&lt;p&gt;指的是logger的輸出等級或是訊息的顯示等級。 訊息輸出的等級必需高於設定檔指定的顯示等級，訊息才會被記錄下來。例如：&lt;/p&gt;&lt;p&gt;如果設定檔中(以log4j為例)是這樣設定：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;log4j.appender.foo=info
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這是說明了「我有個logger叫foo。 他的輸出等級是&lt;strong&gt;info&lt;/strong&gt;」。 而在Java中這樣撰寫：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;foologger.info(&amp;quot;this is info message&amp;quot;);
foologger.debug(&amp;quot;this is detail message&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這說明了我記錄了2個訊息，一個是使用&lt;strong&gt;info&lt;/strong&gt;等級來記錄，一個是&lt;strong&gt;debug&lt;/strong&gt;等級來記錄。 在這樣的配置設定下，只有info等級的訊息會被紀錄下來。&lt;/p&gt;&lt;h2 id=&quot;layout&quot;&gt;Layout&lt;/h2&gt;&lt;p&gt;簡單來說，就是訊息的格式。&lt;/p&gt;&lt;p&gt;舉個範例(還是以log4j為例)：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;log4j.appender.foo.layout.ConversionPattern=[FOO]%d{HH:mm:ss S} %5p %c{1}:%L - %m%n
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;%&lt;/code&gt;後面接的都是參數。 當然，參數的意義，請參見各log framework的文件。&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;我的經驗&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;一個系統裡，可以設置多個logger，讓每個logger，因應不同的情況輸出到不同的地方，或是多個地方。&lt;/strong&gt; 我舉一些例子。&lt;/p&gt;&lt;h2 id=&quot;framework-logger-&quot;&gt;Framework 的logger，通常為最低，而且輸出是畫面&lt;/h2&gt;&lt;p&gt;因為log4j是很老牌的log framework，許多framework 也都會使用。 像Spring或是第三方frameowrk裡的logger我通常會使用最低等級的level ，並且輸出到console appender上。 因為我會需要去看spring裡的變數時，通常是在開發階段。 這樣的階段，也通常會有console可供使用，而這些log資訊，產生完，看看、確認過後就不再用了，就真的可以讓他&lt;em&gt;閃過了無痕&lt;/em&gt;。也不需要存在硬碟裡浪費空間。 當然這樣的logger，訊息量有時候會很大，console如果buffer 不夠，在螢幕中也時常會一起&lt;em&gt;閃過了無痕&lt;/em&gt;，所以看狀況，有時也會放到file裡去。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;log4j.org.springframework=debug, spring
log4j.appender.spring=org.apache.log4j.ConsoleAppender
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h2 id=&quot;-logger-&quot;&gt;對外間接的程式碼中，使用中等級的logger，並保留到實體儲存體&lt;/h2&gt;&lt;p&gt;有時候，我會在與對外系統間接的地方安插logger。這樣的logger，主要是用作稽核，確定傳出去的資料或是接受到的資料是正確的，以釐清資料流責任。如果是外部系統傳入了與規格不合的資料，而導致回傳結結果錯誤，或甚至系統錯錯誤，馬上就可以提出證據，立馬將責任丟出去， XD。 這樣的logger，我通常都會使用info等級，並存放在DAO中，因為通常與外部系統間接的地方，都是在DAO。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;規格不確定的地方 使用略高等級&lt;/h2&gt;&lt;p&gt;比方說設計階段明明說好了這個變數只有4種值，執行時或上線後卻跑出了第5種，這時候我除了會做error handle(可能會讓程式繼續跑下去)，也會使用略高的訊息輸出等級來記錄訊息， 這樣可以比較一目了然。 另外像基本資料、或是前提假這類的資訊，如果handle 到error，我也會使用略高等級的等級來紀錄。&lt;/p&gt;&lt;h2 id=&quot;-logger-&quot;&gt;稽核型態的logger，會使用中高等級來記錄訊息&lt;/h2&gt;&lt;p&gt;比方說如果我想監控系統使用人員，可能就會使用這樣的logger：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;logger.warn(&amp;quot;一個人從&amp;quot;+ logFromIp+ &amp;quot;執行了&amp;quot;+requestUrl);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如同之前稽核作用的log，這樣的log訊息，自然也是會放在實體儲存體中。&lt;/p&gt;&lt;p&gt;我在前面有的一些地方，有提到&lt;strong&gt;實體儲存體&lt;/strong&gt;，而不使用&lt;strong&gt;檔案&lt;/strong&gt;，是因為有時候可以依情況，將log儲入檔案或是DB。&lt;/p&gt;&lt;h2 id=&quot;exception-error-&quot;&gt;Exception使用error訊息等級&lt;/h2&gt;&lt;p&gt;Try-catch的地方，我一般會使用error訊息等級來記錄Exception；而且Exception都會記錄stacktrace。 在Layout的部份，也都會儘量可以指出程式碼行號。 指出程式碼行號有2個好處： 第一，可以很明確的指出錯誤拋出的程式碼。 第二，如果你發現訊息的行號與程式的行號不一致，這只表示一件是： 你現在看的程式碼與實際執行的程式是不同的。 例如：這個訊息在程式碼中，明明是第3行，可是log卻說在第5行，這表示你上錯檔案、或是執行到不同版本的程式。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;3 logger.info(&amp;quot;這個訊息應在第三行&amp;quot;);

[FOO] 20:44:35 105 info Test:5 - 這個訊息應在第三行
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;-&quot;&gt;開發時期資訊使用低訊息等級&lt;/h2&gt;&lt;p&gt;一般來說IDE的Debug模式是可以顯示執行中變數的值，但這通常都是限制執行環境必需要在IDE中才可以。不過當你無法在IDE中執行的時候，就必需要靠傳統的「插旗標」的方法，來顯示變數值。通常像「插旗標」用的log，我都會使用最低等級來顯示─debug，或甚至是trace。 有時候，在追蹤程式的流程時，也是同樣會使用低級的logger。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;logger.tracert(&amp;quot;變數A:&amp;quot; + a );
logger.debug(&amp;quot;取得資料庫連線&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;因為這些情況，大多在開發時期所會需要使用到的資訊，不需要在上線時也浪費效能與系統資源來顯示這些訊。 簡單來說，&lt;strong&gt;開發時期所需要用的資訊，我都會使用低訊息等級來輸出&lt;/strong&gt;。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;依照不同的環境，有不同的配置檔&lt;/h2&gt;&lt;p&gt;例如在開發環境下，我會將輸出全部導到console上， 而在正式環境中，則將輸出全部導到檔案，或是資料庫中。 當然，這必需搭配相關的佈署策略與工具，可以使用相同的source code，依不同的執行命令或參數，使用不同的設定檔，產生2種不同的war檔。 實務上的作法，當然是另外一篇文章的事。 &lt;em&gt;如果輸出是檔案的話，也一般都會設成一天一個檔&lt;/em&gt;。 &lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;結語&lt;/h1&gt;&lt;p&gt;這篇文章大概介紹了logger的組成成份，還有對我平常如何使用logger的一些經驗匯整。 使用方式並不是絕對，只是我個人的經驗與看法，有其他的方式或經驗也歡迎大家提出來一起討論。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/4593854775571520431/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/4593854775571520431' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4593854775571520431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/4593854775571520431'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/blog-post.html' title='好的日誌配置讓你上天堂，不好日誌配置會讓你抓狂：我如何使用日誌配置'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-7721357928362053775</id><published>2015-03-06T00:25:00.000+08:00</published><updated>2015-03-06T00:32:06.847+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Perl"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Perl 教學 102</title><content type='html'>&lt;p&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8632/16724267995_29700fa1e4_o.png&quot; /&gt;&lt;/p&gt;&lt;p&gt;如果你搞不清楚Perl與Pe&lt;strong&gt;a&lt;/strong&gt;rl的 的區別，請先參考 &lt;a href=&quot;http://taichitech.blogspot.tw/2015/03/perl-101.html&quot;&gt;Perl101&lt;/a&gt;。 Perl101 會告訴你什麼是Perl、如何安裝，與一個簡單的Perl Hello World。 想更了解Perl？&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;請繼續看下去…&lt;/p&gt;&lt;h1&gt;註解&lt;/h1&gt;&lt;ul&gt;&lt;li&gt;註解使用 &lt;code&gt;#&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Perl沒有區域註解。&lt;/li&gt;
&lt;/ul&gt;&lt;h1&gt;資料結構&lt;/h1&gt;&lt;p&gt;專業術語叫「資料結構」，不過我覺得用白話一點的資料型態，會比較好標示出這一段的重點。&lt;/p&gt;&lt;p&gt;Perl 只有3種資料型態: &lt;strong&gt;Scalar&lt;/strong&gt;(純量變數)、&lt;strong&gt;Array&lt;strong&gt;(串列變數)和&lt;/strong&gt;Hash&lt;/strong&gt;(雜湊)；沒有Boolean、日期，也不區分浮點數、整數之類的。&lt;/p&gt;&lt;p&gt;&lt;code&gt;Scalar&lt;/code&gt;是文字、數字，或是參考(Reference) 。&lt;code&gt;Array&lt;/code&gt;是一堆scalar在一塊，用數字做索引；&lt;code&gt;Hash&lt;/code&gt;則是用KEY做索引，而且沒有固定順序，有點類似&lt;code&gt;Map&lt;/code&gt;或是&lt;code&gt;Dictionary&lt;/code&gt;。 Scalar使用&lt;code&gt;$&lt;/code&gt;做為變數開頭，例如：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;$thisIsScalar=100; # 最基礎的Scalar變數
$thisIsArray[0]=100; # thisIsArray變數的第一個元素
$thisIsHash{&amp;quot;K&amp;quot;}=100; #KEY值是K的元素
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;如果你想表示一整個Array或是Hash，則使用&lt;code&gt;@&lt;/code&gt;或是&lt;code&gt;%&lt;/code&gt;。例如：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;@thisIsArray;
%thisIsHash;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;但你是也可以特別的指出裡面的某些元素，例如：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;@thisIsArray[0]=100;
%thisIshash{&amp;quot;K&amp;quot;}=100;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;於是初學者就會很容易搞不清楚&lt;code&gt;@&lt;/code&gt;與&lt;code&gt;$&lt;/code&gt;的差別了… XD。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;$thisIsArray[0];
@thisIsArray[0];
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;其實這兩種寫法或許是不同的語意，但是是相同的東西。Perl的一個好處就是相同的東西可以有不同的寫法，怎麼寫都對，非常容易上手！ 不過這個卻是我在 &lt;a href=&quot;http://taichitech.blogspot.tw/2015/03/perl-101.html&quot;&gt;Perl 101&lt;/a&gt; 中所提到的，我所不喜歡Perl的原因。 &lt;strong&gt;同樣一件事情，有不同的寫法&lt;/strong&gt;。 &lt;/p&gt;&lt;h2&gt;陣列(Array)與串列(List)的差別&lt;/h2&gt;&lt;p&gt;駱馬書(Perl界的聖經，Oreilly出版)上說: &lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;基本上(&amp;quot;A&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;)這些是串列(list)。當串列assign 給一個變數時，這個變數稱陣列。&lt;br /&gt;
串列是純量的有序集合。陣列是一個包含了串列的變數。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;老實說，一開始看到這段翻譯時，實在不知道在翻什麼。看完整章節後，覺得大概的意思是：&lt;strong&gt;Array是表示整個集合，使用&lt;code&gt;@&lt;/code&gt;作為識別，如果你想表達單一元素，則使用&lt;code&gt;$&lt;/code&gt;來表示&lt;/strong&gt;。 致於詳細的內容就要請各位參照書籍了。 &lt;/p&gt;&lt;p&gt;基本上在大多數有關於Array的操作，一般都是使用$thisIsArray[$i]來進行。 因為前面有提到，原則上@thisIsArray是表示整個串列。不過你可以在@thisIsArray後面，使用&lt;code&gt;[&lt;/code&gt;&lt;code&gt;]&lt;/code&gt;來特別示要取得一個陣列的那些元素，所以&lt;code&gt;@thisIsArray[0]&lt;/code&gt;其實是&lt;code&gt;@thisIsArray[0,2,5]&lt;/code&gt;的變型。&lt;/p&gt;&lt;p&gt;有了這個概念後，同樣可以套到Hash上去。 我們同樣可以用&lt;code&gt;%thisIsHash&lt;/code&gt;來表示&lt;code&gt;$thisIsHash{&amp;quot;K&amp;quot;}&lt;/code&gt;+&lt;code&gt;$thisIsHash{&amp;quot;L&amp;quot;}&lt;/code&gt;。 &lt;/p&gt;&lt;h1&gt;初始化&lt;/h1&gt;&lt;p&gt;直接舉例。 不過如果想學好Perl的朋友，最好是開文字編輯器， 自己實際全部敲一次。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;$thisIsScalar = &amp;quot;A&amp;quot;;
$thisIsScalar =100;
$thisIsArray[0] = 100;
$thisIsArray[1] = 200;
@thisIsArray = (100,200);
$thisIsHash{&amp;quot;K&amp;quot;} = 100;
$thisIsHash{&amp;quot;L&amp;quot;} = 200;
%thisIsHash = (&amp;quot;M&amp;quot; =&amp;gt; 100,&amp;quot;N&amp;quot; =&amp;gt;200);
@thisIsHash{&amp;quot;O&amp;quot;,&amp;quot;P&amp;quot;}  = (100,200); 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;這裡歸納一下。 原則上，一般的變數操作， 單一給定型的，都是針對Scalar進行賦值。所以 不管是 thisIsScalar、thisIsArray、thisIsHash 都是使用&lt;code&gt;$&lt;/code&gt;。  一次給定型的，Array使用&lt;code&gt;(&lt;/code&gt;、&lt;code&gt;)&lt;/code&gt;給值，Hash使用&lt;code&gt;()&lt;/code&gt;加&lt;code&gt;=&amp;gt;&lt;/code&gt;來賦值。 &lt;/p&gt;&lt;p&gt;這裡要特別指出&lt;code&gt;@thisIsHash{&amp;quot;O&amp;quot;,&amp;quot;P&amp;quot;}&lt;/code&gt;，他的前面綴符號之所會是&lt;code&gt;@&lt;/code&gt;，是因為他一次初始化了2個值。&lt;/p&gt;&lt;h2&gt;比較特殊的操作&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;$#x 取@x最後的index值&lt;/li&gt;
&lt;/ul&gt;&lt;h1&gt;網路上的文章百百種，還是要看看官網的文件&lt;/h1&gt;&lt;p&gt;一開始學習的Perl時，會依自己以前的經驗來解讀Perl。 不過在學習的過程中，建議還是回過頭來，閱讀一下官方文件，去了解Larry(Perl發明人)的想法。其實整個學習Perl的過程，可以說是就是學習Larry的想法，而官網的文件就是最原生、表達作者想法的媒界。&lt;/p&gt;&lt;p&gt;(不知道有沒有續)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/7721357928362053775/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/7721357928362053775' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7721357928362053775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7721357928362053775'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/perl-102.html' title='Perl 教學 102'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-201732473878389096</id><published>2015-03-03T11:47:00.000+08:00</published><updated>2015-03-03T11:47:09.381+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Perl"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>Perl 教學 101</title><content type='html'>&lt;p&gt;因為工作的關係，需要使用Perl做為開發語言，所以最近花了一些時間，鑽研一下Perl這個語言！&lt;a name=&#39;more&#39;&gt;&lt;/a&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;Perl是一種&lt;strike&gt;老掉牙&lt;/strike&gt;歷史悠久的語言了，不過他跟Pe&lt;strong&gt;a&lt;/strong&gt;rl沒有關係。 能夠有這麼長的歷史，一定有他的優點。 不過這樣的優點，從我的觀念來看，並不是一件好事。 詳細的情形，我在後面會描述。 基於與我觀念背道而馳的理由， 我應該不會太花時間繼續鑽研下去， 能應付工作已經足夠。 不過既然已經花時間鑽研，就記錄一下。&lt;/p&gt;&lt;h2 id=&quot;-&quot;&gt;故事的開始是這樣的&lt;/h2&gt;&lt;p&gt;Perl是script型態的程式語言。 最初設計者拉里·沃爾（Larry Wall）。最初發行時間約在 1987年12月18日發表。 目前，版本中包含Perl 5以及Perl 6（開發中）。&lt;/p&gt;&lt;p&gt;Perl借用了C、sed、awk、shell指令碼以及很多其他程式語言的特性。 談到Perl有幾個特性：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;內部整合了正規表式的功能，&lt;/li&gt;&lt;li&gt;巨大的第三方代碼庫CPAN。 你可以在CPA找到各代各樣的模組。 截至2012年4月，它已經有超過9500個作者寫的24500多個模組。&lt;/li&gt;&lt;li&gt;靈活的撰寫風格。 (不過這卻是我最不喜歡-或者是說唯一不喜歡-的地方。)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;2000年開始，目前拉里·沃爾開始開發Perl 6，來作為Perl的後繼；不過，Perl 6語言的語法有很多轉變，所以Perl 6被視為Perl家族中的另一個語言。&lt;/p&gt;&lt;p&gt;Perl語言的應用範圍很廣，很多是使用於CGI。 而除了CGI以外，還被廣範應用於圖形編程、系統管理、網路編程、金融、生物以及其他領域。 由於其靈活性，Perl被稱為手稿語言中的瑞士軍刀。&lt;/p&gt;&lt;p&gt;(引用自Wiki)&lt;/p&gt;&lt;h1 id=&quot;-&quot;&gt;你只會做一次的事&lt;/h1&gt;&lt;h2 id=&quot;-ide&quot;&gt;選擇開發工具：推薦IDE&lt;/h2&gt;&lt;p&gt;幾乎所有的語言，都可以使用純文字編輯器作為開發工具，而不需要特別去使用IDE。Perl也是一樣。只要使用你所習慣的純文字編輯器就可以。 不過有一個訴求：&lt;strong&gt;要能夠正確辨識檔案格式&lt;/strong&gt;，例如檔案存檔是存成UTF8 或是 ANSI 等格式。 這不是限制，但是最好是可以做到，不然在開發或是學習過程中， 可能會發生許多你搞不清楚原因的問題。 因為有許多人是使用UltraEdit做為預設的純文字編輯器，而UltraEdit預設則是使存成windows預設使用的 ANSI 格式。 如果使用UltraEdit的朋友，稍微注意一下即可。&lt;/p&gt;&lt;p&gt;如果硬要推薦一款IDE的話，我推薦Padre這款IDE。 理由是：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;也有人推薦它，應該還好上手。&lt;/li&gt;&lt;li&gt;免費。&lt;/li&gt;&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://dwimperl.com&quot;&gt;這是Padre的官網&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.vixual.net/blog/archives/846&quot;&gt;這裡有前輩寫的使用入門&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;-&quot;&gt;安裝&lt;/h2&gt;&lt;p&gt;請到官網下載Perl的執行環境。 Perl的官網是&lt;a href=&quot;www.perl.org&quot;&gt;www.perl.org&lt;/a&gt;。 目前(2015/02/25) Perl 官網提供下載的版本，除了作業系統不同外，Windows 版還特別多了一個StrewBerry版。 如果你是Windows 作業系統，請下載AciveSState版。 下載時，請注意自己的32/64版本。 下載下來後，無腦的執行下一步即可。&lt;/p&gt;&lt;h1 id=&quot;hello-world&quot;&gt;Hello World&lt;/h1&gt;&lt;p&gt;大多數的語言教學，一開始就是給你一個Hello World。 基本上Hello World沒有任何用處，但是它可以讓你對那種語言，可以有一些親切感。&lt;/p&gt;&lt;p&gt;Perl的Hello World很簡單：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;用你的文字編輯器，撰寫以下的程式碼，存檔(檔名為HelloWorld.pl)。 記得存成UTF8格式。&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;print &amp;quot;Hello World, Perl!!&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;在DOS模式下執行&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;perl HelloWorld.pl
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;順帶一提，如果印出的字樣有中文，則必需存成ANSI格式。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;print &amp;quot;你好, Perl!!&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;當然，實務上不是這樣做。 不過這是屬於進階的議題。 至於建議存成UTF8或是ANSI 則是看需求。 &lt;/p&gt;&lt;p&gt;(待續)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/201732473878389096/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/201732473878389096' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/201732473878389096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/201732473878389096'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2015/03/perl-101.html' title='Perl 教學 101'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-7839648714687455257</id><published>2014-11-18T00:04:00.000+08:00</published><updated>2014-11-18T00:04:10.745+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>進行Java 除法運算時，不提醒你很容易埋的bug</title><content type='html'>&lt;p&gt;Java的除法，有一些很基礎的操作，這些操作平常不會發生問題，但一旦發生真的是因為這些操作導致的bug，也真的是很難查覺得出來。&lt;/p&gt;&lt;p&gt;小學數學規定：「除數不能為零」 XD，原來事情不像小學生想得這麼簡單。 &lt;/p&gt;&lt;div align=&quot;center&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7486/15189716744_720cce16f2_o.png&quot; alt=&quot;&quot;&gt;&lt;br/&gt;&lt;br /&gt;
&lt;span style=&quot;font-size:small&quot;&gt;「除數不能為零」不是只是『規定』，他其實是有不得以的原因&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;Wiki 有一堆不是專業數學碩士看不懂的證明，說明它不只是「不能為零」，而且是「必需『不』為零」，不然1會等於2…。 &lt;/p&gt;&lt;p&gt;不過不管除數可不可以為零，在Java裡，其實是可以的…。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public class FloatOperation {
    private static Logger logger = LoggerFactory.getLogger(FloatOperation.class);

    public static void main(String[] args) {
        div(&amp;quot;案例1&amp;quot;, 6, 4);
        div(&amp;quot;案例2&amp;quot;, 2, 3.0);
        div(&amp;quot;案例3&amp;quot;, 2.3, 0.0);
        div(&amp;quot;案例4&amp;quot;, 2.3, -0.0);
        div(&amp;quot;案例5&amp;quot;, 0.0, 0.0);
        div(&amp;quot;案例6&amp;quot;, 0.0, -0.0);
        div(&amp;quot;案例7&amp;quot;, 0.0, 0.1);
        div(&amp;quot;案例8&amp;quot;, 0.0, -0.1);
    }

    public static Double div(String title, int a, int b) {
        double x = a / b;
        logger.trace(&amp;quot;{}: {}/{}={}&amp;quot;, title, a, b, x);
        return x;
    }

    public static Double div(String title, double a, double b) {
        double x = a / b;
        logger.trace(&amp;quot;{}: {}/{}={}&amp;quot;, title, a, b, x);
        return x;
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以上範例程式執行結果如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;278 [TRACE] t.b.b.FloatOperation[22]: 案例1: 6/4=1.0
281 [TRACE] t.b.b.FloatOperation[28]: 案例2: 2.0/3.0=0.6666666666666666
281 [TRACE] t.b.b.FloatOperation[28]: 案例3: 2.3/0.0=Infinity
282 [TRACE] t.b.b.FloatOperation[28]: 案例4: 2.3/-0.0=-Infinity
282 [TRACE] t.b.b.FloatOperation[28]: 案例5: 0.0/0.0=NaN
282 [TRACE] t.b.b.FloatOperation[28]: 案例6: 0.0/-0.0=NaN
282 [TRACE] t.b.b.FloatOperation[28]: 案例7: 0.0/0.1=0.0
282 [TRACE] t.b.b.FloatOperation[28]: 案例8: 0.0/-0.1=-0.0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;簡單來說, 做幾個結論：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;除數為「整數」0 時會丟異常。&lt;/li&gt;
&lt;li&gt;除法的時候，除數、被除數任一為浮點數，結果即為浮點數。 若都是整數，則結果為整數，就如同浮點數轉型為整數一樣，小數點後的東西都被刪光了。案例1即為例子。&lt;/li&gt;
&lt;li&gt;0.0做除數而被除數不為0時，得到的是一個分正負的無窮大(Infinity)，看是+0f或是-0f。&lt;/li&gt;
&lt;li&gt;當兩個數的絕對值均爲0.0時候，商等于NaN。&lt;/li&gt;
&lt;li&gt;如一般所認知的，0.0/任何不為0的數，都為0。而且這是有帶正負符號位的；符號位由被除數決定。當然，如果負負的話，還是會得正。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;code&gt;Double&lt;/code&gt;、&lt;code&gt;Float&lt;/code&gt;都有提供static 與非static 的inFinite與NaN的判斷API，在使用除法運算時，要使用這些API來進行安全判斷. &lt;/p&gt;&lt;p&gt;Double、Float類中都提供了一些常數，可以用這些常數來判斷正無限大或負無限大。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;public static final double POSITIVE_INFINITY = 1.0 / 0.0; 
public static final double NEGATIVE_INFINITY = -1.0 / 0.0; 
public static final double NaN = 0.0d / 0.0; &lt;/code&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/7839648714687455257/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/7839648714687455257' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7839648714687455257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/7839648714687455257'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2014/11/java-bug.html' title='進行Java 除法運算時，不提醒你很容易埋的bug'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20849359.post-356771327994231527</id><published>2014-11-14T01:00:00.000+08:00</published><updated>2014-11-14T01:00:16.850+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="jsoup"/><category scheme="http://www.blogger.com/atom/ns#" term="技術"/><title type='text'>如何使用Jsoup解析 Html</title><content type='html'>&lt;p&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7527/15595188807_4a8597ee50_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;話說最近剛好有解析html 的需求。 今天介紹一個工具，可以使用Java ，簡單的解析Html─Jsoup。&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://jsoup.org/&quot;&gt;Jsoup官網&lt;/a&gt;&lt;/p&gt;&lt;p&gt;會選擇介紹這套framework的原因幾個：&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;p&gt;這幾個原因也是我在選擇使用framework的一些考量。 有時候我會加上&lt;strong&gt;老牌(夠成熟)&lt;/strong&gt;這樣的一個考量。 當然，通常夠成熟也會代表著不見得會在持續更新，或是更新速度已經變慢。&lt;/p&gt;&lt;p&gt;Jsoup不只可以解析Html，還可對Html進行一些變更後產出。Jsoup有以下一些特色：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;可以有多種多入，包含URL、File、String。&lt;/li&gt;
&lt;li&gt;有多種方式可以取得結點資訊，包含透過DOM瀏覽方式與使用CSS 的選擇器。&lt;/li&gt;
&lt;li&gt;變更取得的Html原素&lt;/li&gt;
&lt;li&gt;清除使用者所提交的一些資訊&lt;/li&gt;
&lt;li&gt;產出所變更的Html&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;(來源：官網)&lt;/p&gt;&lt;h1 id=&quot;-html&quot;&gt;如何解析HTML&lt;/h1&gt;&lt;p&gt;假設我有一個網頁：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;聊天室列表&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
&amp;lt;form id=&amp;quot;input_form&amp;quot;&amp;gt;
    &amp;lt;a href=&amp;quot;main.html&amp;quot;&amp;gt; 回首頁 &amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;
    &amp;lt;a href=&amp;quot;next.html&amp;quot;&amp;gt; 到下一頁 &amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;
&amp;lt;input name=&amp;quot;button&amp;quot; type=&amp;quot;button&amp;quot; id=&amp;quot;button&amp;quot; value=&amp;quot;登出&amp;quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;聊天室ID&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;聊天室名稱&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;123456T890&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;chatRoom.html&amp;quot;&amp;gt;洛基&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;12X4567890&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;chatRoom.html&amp;quot;&amp;gt;阿波羅&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
 &amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;他執行起來結果看起來像這樣子：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm6.staticflickr.com/5609/15781637332_5a09f3a4b3_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;解析Html的範例程式碼會是這個樣子。&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;File input = new File(&amp;quot;input.html&amp;quot;);
Document doc = Jsoup.parse(input, &amp;quot;UTF-8&amp;quot;);
Element content = doc.getElementById(&amp;quot;input_form&amp;quot;);
Elements links = content.getElementsByTag(&amp;quot;a&amp;quot;);
for (Element link : links) {
    String linkHref = link.attr(&amp;quot;href&amp;quot;);
    String linkText = link.text();
    logger.trace(&amp;quot;link href:{}, link text:{}&amp;quot;, linkHref, linkText);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;執行結果會是像這個樣子：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;299 [TRACE] t.b.j.JsoupTest[23]: link href:main.html, link text:回首頁
302 [TRACE] t.b.j.JsoupTest[23]: link href:next.html, link text:到下一頁&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;要注意的是Element 與 Element&lt;strong&gt;s&lt;/strong&gt; 的差異。 顧名思義，Elements 可以跑迴圈。而 &lt;code&gt;Document&lt;/code&gt;則是一個&lt;code&gt;Element&lt;/code&gt;。 一般而言，除非像id這樣的取法，不然所搜尋出來的, 多半是Element&lt;strong&gt;s&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;Jsoup這個tool，主要的進入點是&lt;code&gt;Jsoup.parse(...)&lt;/code&gt;這個API。 &lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;Jsoup.parse(...)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你已經將File轉成String，可以這樣用：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;String htmlSoruce=getHtmlSource();
Document doc = Jsoup.parse(htmlSoruce);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;parse&lt;/code&gt;可以有許多種輸入，甚至可以直接輸入URL；不過似乎還無法丟參數。 更多的使用方式，當然需要詳見 &lt;a href=&quot;http://jsoup.org/apidocs/&quot;&gt;API&lt;/a&gt; 啦。&lt;/p&gt;&lt;h1 id=&quot;-html-&quot;&gt;如何取得Html物件&lt;/h1&gt;&lt;p&gt;Jsoup取得tag的方式有很多種，使用id只是其中的一種。 也可以使用&lt;code&gt;byClass&lt;/code&gt;、&lt;code&gt;byTag&lt;/code&gt;。 不過我喜歡用&lt;em&gt;CSS selector&lt;/em&gt;的方式來取得。 &lt;/p&gt;&lt;p&gt;什麼是CSS selector？ 他是一種CSS 在搜尋tag時，所使用的語法。 &lt;/p&gt;&lt;p&gt;就如同一般指定tag的方式，CSS也有他搜尋tag的方式，來指定特定tag所要呈現的style。 CSS的搜尋語法，跟jquery似乎是一樣的。 這麼複雜的語法，跟行院的動能推動方案一樣，很難用簡單的幾句話說明，&lt;strike&gt;(其實我也不太會)&lt;/strike&gt;，所以各位如果有需要，可以詳參jsoup &lt;a href=&quot;http://jsoup.org/apidocs/org/jsoup/select/Selector.html&quot;&gt;&lt;code&gt;Selector&lt;/code&gt;的Java doc&lt;/a&gt; 。 &lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7546/15756783356_f2e0a8073f_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;CSS selector 語法要學其實不會太困難。 會的話當然好，但是如果真沒時間學習的話，我們有其他的變通方式。 後面我會介紹。&lt;/p&gt;&lt;p&gt;接下來，談一下範例。&lt;/p&gt;&lt;p&gt;比方說我想取得表格(tbody)的內容，需要先取得tbody element。 Tbody element的selector syntax是這樣：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;body &amp;gt; table &amp;gt; tbody&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你閱讀程式碼的功力夠高的話，加上已經了解CSS selector語法的話，可以直接撰寫。 小弟不才，使用偷吃步的方式：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://farm6.staticflickr.com/5603/15756391356_f27709f1ff_o.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;div align=&quot;center&quot; style=&quot;font-size: small; &quot;&gt;Google Chrome 功能列→更多工具→開發人員工具，點選右鍵→Copy CSS path&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;我們可以使用Google Chrome browser，點選 功能列→更多工具→開發人員工具，然後在要取得的元素上點選右鍵。 &lt;/p&gt;&lt;p&gt;於是取得表格內容的程式碼範例會是這樣：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;File input = new File(&amp;quot;input.html&amp;quot;);
// String htmlSoruce=getHtmlSource();
Document doc = Jsoup.parse(input, &amp;quot;utf-8&amp;quot;);
Elements tbodys = doc.select(&amp;quot;body &amp;gt; table &amp;gt; tbody&amp;quot;);
Element tbody = tbodys.get(0);
for (Element tr : tbody.children()) {
    logger.trace(tr.text());
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;執行結果如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot; &gt;&lt;code&gt;336 [TRACE] t.b.j.JsoupTest[23]: 聊天室ID 聊天室名稱
340 [TRACE] t.b.j.JsoupTest[23]: 123456T890 洛基
340 [TRACE] t.b.j.JsoupTest[23]: 12X4567890 阿波羅&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-&quot;&gt;更多請參考&lt;/h1&gt;&lt;ul&gt;&lt;li&gt;要想摸透一套工具，&lt;a href=&quot;http://jsoup.org/&quot;&gt;官網&lt;/a&gt;的資訊是不可獲缺的。 &lt;/li&gt;
&lt;li&gt;其實也有其他可以解析Html的framework，可以參考： &lt;a href=&quot;http://java-source.net/open-source/html-parsers&quot;&gt;Java-source.net&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;你可能會留意到，明明source code沒有tbody，可是瀏覽器卻會顯示tbody。 這是因為tbody是table的必要原素，即使source code沒有，瀏覽器在處理/呈現時，還是會當他是存在的。 所以當我們在使用工具檢視原素時，還是會存在tbody這個tag，而檢視原始碼時，卻沒有這個tag。&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://taichitech.blogspot.com/feeds/356771327994231527/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/20849359/356771327994231527' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/356771327994231527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20849359/posts/default/356771327994231527'/><link rel='alternate' type='text/html' href='http://taichitech.blogspot.com/2014/11/jsoup-html.html' title='如何使用Jsoup解析 Html'/><author><name>施佑達</name><uri>http://www.blogger.com/profile/17034670626210321957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>