﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-John Jiang-随笔分类-原创</title><link>http://www.blogjava.net/jiangshachina/category/50359.html</link><description>a cup of Java, cheers!</description><language>zh-cn</language><lastBuildDate>Fri, 14 Oct 2016 08:08:18 GMT</lastBuildDate><pubDate>Fri, 14 Oct 2016 08:08:18 GMT</pubDate><ttl>60</ttl><item><title>探索HTTP/2: 流的状态(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 08 Oct 2016 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431871.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431871.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: 流的状态</span></strong></div>
<span style="font-size: 10pt;"><a href="http://www.blogjava.net/jiangshachina/category/55120.html">探索HTTP/2系列</a>的第四篇文章，解读了HTTP/2流的状态，以及状态之间的转化。(2016.10.09最后更新)</span><br />
<br />
<strong><span style="font-size: 12pt;">1. 概述</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; HTTP/2的流(Stream)是有状态的。当客户端或服务器端在使用某个流去发送或接收特定帧(Frame)或包含特定标签(Flag)的帧时，会引起流的状态的转化。<a href="https://tools.ietf.org/html/rfc7540">HTTP 2协议</a>定义的流状态，如下所示：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send&nbsp;PP&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;PP<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">--------|&nbsp;&nbsp;idle&nbsp;&nbsp;|--------.<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;H&nbsp;/&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">------|&nbsp;reserved&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;H&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;reserved&nbsp;|------.<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(local)&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(remote)&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recv&nbsp;ES&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;send&nbsp;H&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">-------|&nbsp;&nbsp;open&nbsp;&nbsp;|-------.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;H&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;half&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;half&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;closed&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;R&nbsp;/&nbsp;&nbsp;|&nbsp;&nbsp;closed&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(remote)&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(local)&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;ES&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recv&nbsp;ES&nbsp;/&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;R&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send&nbsp;R&nbsp;/&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;send&nbsp;R&nbsp;/&nbsp;&nbsp;`-----------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;-----------'&nbsp;&nbsp;send&nbsp;R&nbsp;/&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;closed&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">`-----------------------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;----------------------'<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+<br />
<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;send:&nbsp;&nbsp;&nbsp;endpoint&nbsp;sends&nbsp;this&nbsp;frame<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;recv:&nbsp;&nbsp;&nbsp;endpoint&nbsp;receives&nbsp;this&nbsp;frame<br />
<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;H:&nbsp;&nbsp;HEADERS&nbsp;frame&nbsp;(with&nbsp;implied&nbsp;CONTINUATIONs)<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;PP:&nbsp;PUSH_PROMISE&nbsp;frame&nbsp;(with&nbsp;implied&nbsp;CONTINUATIONs)<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;ES:&nbsp;END_STREAM&nbsp;flag<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;R:&nbsp;&nbsp;RST_STREAM&nbsp;frame</span></div>
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">总的说，HTTP/2为流的整个生命周期定义了7种状态：idle，reserved (local)，reserved (remote)，open，half closed (local)，half closed (remote)和closed。当一端发送或接收头部块(由一个HEADERS/PUSH_PROMISE帧和紧随它的零到多个CONTINUATION帧组成的集合)或RST_STREAM帧，或包含有END_STREAM标签的帧(HEADERS和DATA)之后，将改变流的状态。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 流的状态基于各端自己的视角。由于帧的传输会有网络延迟，在同一时刻，不同端认为的流的状态可能是不同的。比如，当发送端使用一个处于idle状态的流发送一个不包含END_STREAM标签的HEADERS帧之后会立即认为该流处于open状态，但此时接收端尚未得到该HEADERS帧，所以在那一时刻，接收端依然认为该流的状态是idle。</span><br />
<br />
<strong><span style="font-size: 12pt;">2. idle</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 所有的流在创建之初都处于idle状态。处于idle状态的流，只允许被用于发送HEADERS帧，但可以被用于接收HEADERS和PRIORITY帧。在一端使用该状态的流发送或接收HEADERS帧之后，该端会认为此流的状态转变为open。接收PRIORITY帧不会改变流的状态。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个idle状态的流可被另一个流通过发送/接收PUSH_PROMISE帧保留着，使其在将来被用于服务器端推送。被保留的流的状态则从idle变为reserved (local/remote)。</span><br />
<br />
<strong><span style="font-size: 12pt;">3. open</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 处于open状态的流可被用于发送任何类型的帧。使用该状态的流去发送/接收包含有END_STREAM标签的帧(HEADERS和DATA)之后，会使该流的状态变成half closed (local/remote)。使用open状态的流发送或接收RST_STREAM帧之后，则会使它的状态转变为closed。</span><br />
<br />
<strong><span style="font-size: 12pt;">4. half closed (local/remote)</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 状态half closed (local)与half closed (remote)中的local与remote的区别，完全是基于各端自己的视角。对于同一个流的两端，如果一端认为这个流的状态是half closed (local)，那么另一端只能认为这个流的状态是half closed (remote)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 处于half closed (local)状态的流只能被用于发送WINDOW_UPDATE，PRIORITY和RST_STREAM帧，但可以被用于接收任何类型的帧。相对应地，处于half closed (remote)状态的流只能被用于接收WINDOW_UPDATE，PRIORITY和RST_STREAM帧，但可以被用于发送任何类型的帧。</span><br />
<br />
<strong><span style="font-size: 12pt;">5. reserved (local/remote)</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 与half closed (local/remote)状态相似，reserved (local/remote)状态中的local与remote也是基于流两端各自的视角。更具体的是，服务器端发送PUSH_PROMISE将一个idle状态的流保留着以用于未来的推送，并视这个被保留的流的状态为reserved (local)，而客户端则视这个流的状态为reserved (remote)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 服务器端使用reserved (local)状态的流向客户端发送HEADERS帧。该HEADERS帧就是服务器端推送(Server Push)中被推送的响应的头部。当发送了HEADERS帧之后，服务器端将视该流的状态为half closed (remote)。</span><br />
<span style="font-size: 10pt;">相应地，客户端通过reserved (remote)状态的流接收到服务器端推送的响应的头部，然后会视该流的状态为half closed (local)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 扩展一下，服务器端推送中被保留的流的状态在变为half closed(local/remote)之后才可能被用于接收/发送被推送的响应的体部，也就是DATA帧。</span><br />
<br />
<strong><span style="font-size: 12pt;">6. closed</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 当一端使用一个流发送或接收到RST_STREAM帧，或通过状态为half closed (local/remote)的流接收/发送包含有END_STREAM标签的帧之后，都会视这个流的状态为closed。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; closed状态预示着流的终结，处于该状态的流将只能发送或接收PRIORITY帧。但有一个特例。即，如果通过使用half closed (local/remote)状态的流去接收或发送包含有END_STREAM标签的帧(HEADERS或DATA)，以使该流的状态变为closed，那么在此之后的较短时间内，仍然可以接收WINDOW_UPDATE或RST_STREAM帧。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-10-08 21:17 <a href="http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索HTTP/2: HPACK协议简述(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 24 Sep 2016 12:29:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431837.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431837.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431837.html</trackback:ping><description><![CDATA[<div>
<div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: HPACK协议简述</span></strong></div><a href="http://www.blogjava.net/jiangshachina/category/55120.html">探索HTTP/2系列</a>的第一篇文章已经介绍了HTTP 2协议，本文则将简述用于HTTP/2头部压缩的<a href="https://tools.ietf.org/html/rfc7541">HPACK协议</a>。(2016.10.01最后更新)<br />
<br />
<strong><span style="font-size: 12pt;">1. 基本原理</span></strong><br />
&nbsp;&nbsp;&nbsp; HPACK头部压缩的基本原理就是使用索引表和<a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman编码</a>。在压缩(编码)与解压(解码)过程，可将指定的头部字段(包含字段名与字段值)存储在索引表中。索引表中的每一个条目由索引(一个整数)，字段名和字段值组成。对于存在索引表中的头部字段，在编码时可以仅使用索引作为该字段的代表，在解码时通过该索引从表中查找出对应的字段。对于其它的字符串，则可以使用Huffman编码进行压缩。<br />
<strong>1.1 索引表</strong><br />
&nbsp;&nbsp;&nbsp; 索引表由静态表与动态表组成。静态表由HPACK协议预定义的61个常用的头部字段组成，其中大部分字段的值为空。静态表是只读的，其中的条目及其位置均不可更改。HPACK协议中的<a href="https://tools.ietf.org/html/rfc7541#appendix-A">附录A</a>列出了全部的静态表条目。动态表也由一系列头部字段组成，但其中的元素不固定，在实际操作中可以插入新的条目，也允许删除已有的条目。<br />
&nbsp;&nbsp;&nbsp; HPACK协议要求静态表与动态表合并在同一个存储空间中，其中静态表置于前部，动态表紧随其后。那么在整个索引表空间中，动态表的第一个条目的索引将是62。动态表的维护原则是先进先出(FIFO)。当向动态表中增加条目时，将总是从第62位插入，原有的条目将全部向右移动一个位置。当从动态表中删除条目时，将总是从最后一位进行删除。<br />
&nbsp;&nbsp;&nbsp; 虽说，协议要求将静态表与动态表合并在一起，但这只是逻辑上的要求。只要动态表的索引是从62开始，那么各个实现可以根据自己的喜好自由地使用存储数据结构。比如，可以将静态表单独放在一个不可变的数组中，而动态表由另一个链表进行存储，这样可能会便于插入和删除条目。只不过，这个链表中元素的下标与动态表中条目的索引之间相差62。<br />
&nbsp;&nbsp;&nbsp; (动态)索引表中的条目允许重复。<br />
<strong>1.2 Huffman编码</strong><br />
&nbsp;&nbsp;&nbsp; Huffman编码是一种用于无损数据压缩的权路径编码算法。在使用该算法时，需要一张所有被编码字符的权重(出现频率)代码表。在对大量的HTTP头部样本进行统计之后，得出了一份适用于HPACK的Huffman代码表，由协议中的<a href="https://tools.ietf.org/html/rfc7541#appendix-B">附录B</a>列出。<br />
<br />
&nbsp;&nbsp;&nbsp; 必须注意的是，HPACK协议并不要求该协议的实现一定要使用索引表，即便某个字段已经存在于索引表中了。而且也不要求一定要对字符串实施Huffman压缩。也就是说，理论上，在编码时可以不对头部字段进行任何形式的压缩，而只需将所有的字符转化成字节形式。<br />
<br />
<strong><span style="font-size: 12pt;">2. 基本数据类型表示法</span></strong><br />
&nbsp;&nbsp;&nbsp; HPACK协议使用的基本数据类型只有两种：整数；字符串。该协议使用整数去表示索引和字符串的长度。头部字段名和值中出现的数字，只会被当作字符串进行处理。<br />
<strong>2.1 整数表示法</strong><br />
&nbsp;&nbsp;&nbsp; HPACK在表示整数时并不是把它简单的转换成二进制形式。因为HPACK希望每一个整数的表示能够从某个8比特位字节(octet，下文将其简写为"字节")中的任何一个比特位开始，但总是要在某个字节的最后一个比特位结束。比如表示127，让它从字节的第一个比特位开始填充，肯定会在最后一个比特位结束，如下图所示：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>如果第一个比特位被其它值占用(用"?"代表)，只能从第二个比特位开始填充呢？结果依然只需要一个字节，如下所示：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | 1 | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
但如果是从第三个比特位开始填充呢？这时会发现一个字节已经不够了，必须要第二个字节。但能否表示成如下形式呢？<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | ? | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span><br />
<span style="font-family: Courier;">| 1 | ? | ? | ? | ? | ? | ? | ? |</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span></div>
</div>
这显然不符合HPACK协议的要求，因为它希望能够在某个字节的最后一个比特位结束这个表示。为达到这一目的，HPACK协议设计出了一种如下图所示的表示法，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | ? | 1 | 1&nbsp;&nbsp; 1&nbsp;&nbsp; 1&nbsp;&nbsp; 1&nbsp;&nbsp; 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span><br />
<span style="font-family: Courier;">| 1 |&nbsp;&nbsp;&nbsp; Value-(2^N-1) LSB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| 0 |&nbsp;&nbsp;&nbsp; Value-(2^N-1) MSB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
第一个字节中能够被用来填充整数表示位的比特位数(上图中的为6)被称为prefix。下面是该表示法的Java语言实现，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;encodeInteger(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;prefix)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(value&nbsp;</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;prefix&nbsp;</span><span style="color: #000000; ">&lt;=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;number&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;prefix)&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(number);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(value&nbsp;</span><span style="color: #000000; ">-=</span><span style="color: #000000; ">&nbsp;number;&nbsp;value&nbsp;</span><span style="color: #000000; ">&gt;=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">;&nbsp;value&nbsp;</span><span style="color: #000000; ">/=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(value&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printBinary(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(String.format(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%8s</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;Integer.toBinaryString(value)).replace(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />
}</span></div>
根据上述算法可知，当prefix为6时，127的表示法如下图所示：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | ? | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span><br />
<span style="font-family: Courier;">| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>2.2 字符串表示法</strong><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp; String Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; String Data (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
HPACK协议使用上图展示的表示法，它由三部分组成：<br />
[1]Huffman标志，表示该字符串是否为Huffman编码，占用一个比特位。<br />
[2]字符串长度，一个使用2.1节所述方法表示的整数，其中prefix为7。<br />
[3]字符串值。若Huffman标志为0，该值就是原始字符串的字节，否则该值是经Huffman编码过的数据。由于经Huffman编码过的数据并不总是能在一个字节的最后一个比特位处结束，所以可能会使用EOS(end-of-string)符号进行填充。<br />
<br />
<strong><span style="font-size: 12pt;">3. 头部字段表示法</span></strong><br />
&nbsp;&nbsp;&nbsp; 有了第2节介绍的基本数据类型的表示法作为基础，现在就可以阐述头部字段的表示法了。HPACK协议将字段表示法分成3种类型。在表示法开头有一个或若干个比特位用于表示类型。<br />
<strong>3.1 已在索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用1个比特位，值为1。索引使用prefix为7的整数表示法。在解码时，不会更新动态表。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Index (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>3.2 将置入索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用2个比特位，值为01。在解码时，会向动态表内插入新条目。这种类型又被分成两种情况：<br />
[1]头部字段名已在索引表中，字段名索引使用prefix为6的整数表示法，而字段值使用字符串表示法。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Index (6+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
[2]头部字段名不在索引表中，字段名和字段值均使用字符串表示法，而第一个字节的后6个比特位均使用0填充。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Name Length (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; Name String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>3.2 暂不置入索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用4个比特位，值为0000。在解码时，不向动态表内插入新条目。这种类型又被分成两种情况：<br />
[1]头部字段名已在索引表中，字段名索引使用prefix为4的整数表示法，而字段值使用字符串表示法。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 0 |&nbsp; Index (4+)&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
[2]头部字段名不在索引表中，字段名和字段值均使用字符串表示法，而第一个字节的后4个比特位均使用0填充。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 0 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Name Length (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; Name String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>3.3 永不置入索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用4个比特位，值为0001。在解码时，不向动态表内插入新条目。这种类型又被分成两种情况：<br />
[1]头部字段名已在索引表中，字段名索引使用prefix为4的整数表示法，而字段值使用字符串表示法。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 1 |&nbsp; Index (4+)&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
[2]头部字段名不在索引表中，字段名和字段值均使用字符串表示法，而第一个字节的后4个比特位均使用0填充。&nbsp; &nbsp;<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Name Length (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; Name String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
&nbsp;&nbsp;&nbsp; 可以发现，3.2节与3.3节中的表示法除了类型标识不同之外，其它的都完全相同。那么它们的区别是什么呢？类型0000表示的字段在经过多次解码与编码时，可能会被某个中介者置入索引表中。而类型0001表示法强调了该字段无论在任何时候都不可置入索引表。类型0001可用于表示包含有敏感信息，如密码，的字段值，以避免对这些值进行压缩时产生的风险。<br />
<br />
<strong><span style="font-size: 12pt;">4. 动态表的管理</span></strong><br />
&nbsp;&nbsp;&nbsp; 动态表中的条目被认为是有尺寸的，其计算公式为：字段名的字节长度+字段值的字节长度+32。字段名/值的长度是指它们的原始字节的长度，而非经过Huffman编码后的字节的长度。<br />
&nbsp;&nbsp;&nbsp; 动态表的尺寸就是其中所有条目的尺寸之和。动态表的最大尺寸是有限的，可以通过下面的整数表示法来通知协议的现实去改变动态表的最大尺寸。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 1 |&nbsp;&nbsp; Max size (5+)&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
&nbsp;&nbsp;&nbsp; 当插入新的条目或改变动态表的最大尺寸时，可能导致已有的一个或多个条目被逐出，甚至清空整个动态表。将动态表的最大尺寸设置为0是合法的，实际上，这是一种常用的清空动态表的途径。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-09-24 20:29 <a href="http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索HTTP/2: 初试HTTP/2(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 20 Sep 2016 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431814.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431814.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431814.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: 初试HTTP/2</span></strong></div><span style="font-size: 10pt;">目前支持HTTP/2的服务器端与客户端实现已有不少，<a href="http://www.blogjava.net/jiangshachina/category/55120.html">探索HTTP/2系列</a>的第二篇就分别以Jetty和curl作为服务器端和客户端，描述了HTTP/2测试环境的搭建过程。本文还将使用这个测试环境去展示Jetty在实现HTTP/2时的一个局限和一个Bug。(2016.09.22最后更新)</span><br /><br /><strong><span style="font-size: 12pt;">1. HTTP/2的实现</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 目前已经有众多的服务器端和客户端实现了对HTTP/2的支持。在服务器端，著名的Apache httpd从2.4.17版，Nginx从1.9.5版，开始支持HTTP/2。在客户端，主流的浏览器，如Chrome，FireFox和IE，的最新版均支持HTTP/2，但它们都只支持运行在TLS上的HTTP/2(即h2)。使用Java语言实现的，则有Jetty和Netty，它们都实现了服务器端和客户端。此处有一份HTTP/2实现的列表：<a href="https://github.com/http2/http2-spec/wiki/Implementations">https://github.com/http2/http2-spec/wiki/Implementations</a></span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另外，还有一些工具支持对HTTP/2的分析与调试，如curl和WireShark。这里也有一份此类工具的列表：<a href="https://github.com/http2/http2-spec/wiki/Tools">https://github.com/http2/http2-spec/wiki/Tools</a></span><br /><br /><strong><span style="font-size: 12pt;">2. 服务器端</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 作为Java程序员，选用一款使用Java语言编写的开源HTTP/2服务器端实现似乎是很自然的结果。实际上，在日后的研究中，我们也需要查看服务器端的源代码。这对于深入地理解HTTP/2，并发现实现中可能的问题，具有现实意义。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文选择Jetty的最新版本9.3.11作为服务器端。Jetty是一个成熟的Servlet容器，这为开发Web应用程序提供了极大便利。而本文第1节中提到的Netty是一个传输层框架，它专注于网络程序。可以使用Netty去开发一个Servlet容器，但这显然不如直接使用Jetty方便。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 安装和配置Jetty是一件很容易的事情，具体过程如下所示。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 假设此时已经下载并解压好了Jetty 9.3.11的压缩文件，目录名为jetty-9.3.11。在其中创建一个test-base子目录，作为将要创建的Jetty Base的目录。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;cd&nbsp;jetty-</span><span style="color: #000000; font-family: Courier;">9.3.11</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">$&nbsp;mkdir&nbsp;test-base<br /></span><span style="color: #000000; font-family: Courier;">$&nbsp;cd&nbsp;test-base</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">在创建Base时，加入支持http，https，http2(h2)，http2c(h2c)和deploy的模块。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;java&nbsp;-jar&nbsp;../start.jar&nbsp;--add-to-startd</span><span style="color: #000000; font-family: Courier;">=</span><span style="color: #000000; font-family: Courier;">http</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">https</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">http2</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">http2c</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">deploy<br /><br /></span><span style="color: #000000; font-family: Courier;">ALERT:&nbsp;There&nbsp;are&nbsp;enabled&nbsp;module(s)&nbsp;with&nbsp;licenses.<br /></span><span style="color: #000000; font-family: Courier;">The&nbsp;following&nbsp;</span><span style="color: #000000; font-family: Courier;">1</span><span style="color: #000000; font-family: Courier;">&nbsp;module(s):<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;+&nbsp;contains&nbsp;software&nbsp;not&nbsp;provided&nbsp;by&nbsp;the&nbsp;Eclipse&nbsp;Foundation!<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;+&nbsp;contains&nbsp;software&nbsp;not&nbsp;covered&nbsp;by&nbsp;the&nbsp;Eclipse&nbsp;Public&nbsp;License!<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;+&nbsp;has&nbsp;not&nbsp;been&nbsp;audited&nbsp;for&nbsp;compliance&nbsp;with&nbsp;its&nbsp;license<br /><br /></span><span style="color: #000000; font-family: Courier;">&nbsp;Module:&nbsp;alpn<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;ALPN&nbsp;is&nbsp;a&nbsp;hosted&nbsp;at&nbsp;github&nbsp;under&nbsp;the&nbsp;GPL&nbsp;v2&nbsp;with&nbsp;ClassPath&nbsp;Exception.<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;ALPN&nbsp;replaces/modifies&nbsp;OpenJDK&nbsp;classes&nbsp;in&nbsp;the&nbsp;java.sun.security.ssl&nbsp;package.<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;http://github.com/jetty-project/jetty-alpn<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;http://openjdk.java.net/legal/gplv2+ce.html<br /><br /></span><span style="color: #000000; font-family: Courier;">Proceed&nbsp;(y/N)?&nbsp;y<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;server&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;(transitively)&nbsp;in&nbsp;${jetty.base}\start.d\server.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;http&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\http.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;ssl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;(transitively)&nbsp;in&nbsp;${jetty.base}\start.d\ssl.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;alpn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;(transitively)&nbsp;in&nbsp;${jetty.base}\start.d\alpn.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;http2c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\http2c.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;https&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\https.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;deploy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\deploy.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;http2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\http2.ini<br /></span><span style="color: #000000; font-family: Courier;">DOWNLOAD:&nbsp;http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/</span><span style="color: #000000; font-family: Courier;">8.1.5</span><span style="color: #000000; font-family: Courier;">.v20150921/alpn-boot-</span><span style="color: #000000; font-family: Courier;">8.1.5</span><span style="color: #000000; font-family: Courier;">.v20150921.jar&nbsp;to&nbsp;${jetty.base}\lib\alpn\alpn-boot-</span><span style="color: #000000; font-family: Courier;">8.1.5</span><span style="color: #000000; font-family: Courier;">.v20150921.jar<br /></span><span style="color: #000000; font-family: Courier;">DOWNLOAD:&nbsp;https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id</span><span style="color: #000000; font-family: Courier;">=</span><span style="color: #000000; font-family: Courier;">master&nbsp;to&nbsp;${jetty.base}\etc\keystore<br /></span><span style="color: #000000; font-family: Courier;">MKDIR:&nbsp;${jetty.base}\webapps<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;Base&nbsp;directory&nbsp;was&nbsp;modified</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">注意，在上述过程中，会根据当前环境变量中使用的Java版本(此处为1.8.0_60)去下载一个对应的TLS-ALPN实现jar文件(此处为alpn-boot-8.1.5.v20150921.jar)，该jar会用于对h2的支持。当启动Jetty时，该jar会被Java的Bootstrap class loader加载到类路径中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 创建一个最简单的Web应用，使它在根目录下包含一个文本文件index，内容为"HTTP/2 Test"。</span><span style="font-size: 10pt;">再包含一个简单的Servlet，代码如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-family: Courier;">package</span><span style="color: #000000; font-family: Courier;">&nbsp;test;<br /><br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;java.io.IOException;<br /><br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.ServletException;<br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.http.HttpServlet;<br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.http.HttpServletRequest;<br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.http.HttpServletResponse;<br /><br /></span><span style="color: #0000ff; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">class</span><span style="color: #000000; font-family: Courier;">&nbsp;TestServlet&nbsp;</span><span style="color: #0000ff; font-family: Courier;">extends</span><span style="color: #000000; font-family: Courier;">&nbsp;HttpServlet&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">long</span><span style="color: #000000; font-family: Courier;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000; font-family: Courier;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">5222793251610509039L</span><span style="color: #000000; font-family: Courier;">;<br /><br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">void</span><span style="color: #000000; font-family: Courier;">&nbsp;doGet(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">throws</span><span style="color: #000000; font-family: Courier;">&nbsp;ServletException,&nbsp;IOException&nbsp;{<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.getWriter().println(</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">Test</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">);<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">void</span><span style="color: #000000; font-family: Courier;">&nbsp;doPost(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">throws</span><span style="color: #000000; font-family: Courier;">&nbsp;ServletException,&nbsp;IOException&nbsp;{<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doGet(request,&nbsp;response);<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-family: Courier;">}</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">web.xml主要是定义了一个Servlet，具体内容如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-family: Courier;">&lt;?</span><span style="color: #ff00ff; font-family: Courier;">xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"</span><span style="color: #0000ff; font-family: Courier;">?&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">web-app&nbsp;</span><span style="color: #ff0000; font-family: Courier;">xmlns</span><span style="color: #0000ff; font-family: Courier;">="http://xmlns.jcp.org/xml/ns/javaee"</span><span style="color: #ff0000; font-family: Courier;">&nbsp;xmlns:xsi</span><span style="color: #0000ff; font-family: Courier;">="http://www.w3.org/2001/XMLSchema-instance"</span><span style="color: #FF0000; "><br /></span><span style="color: #ff0000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;xsi:schemaLocation</span><span style="color: #0000ff; font-family: Courier;">="http://xmlns.jcp.org/xml/ns/javaee&nbsp;http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"</span><span style="color: #FF0000; "><br /></span><span style="color: #ff0000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;metadata-complete</span><span style="color: #0000ff; font-family: Courier;">="false"</span><span style="color: #ff0000; font-family: Courier;">&nbsp;version</span><span style="color: #0000ff; font-family: Courier;">="3.1"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">welcome-file-list</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">welcome-file</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">index</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">welcome-file</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">welcome-file-list</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">test</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-class</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">test.TestServlet</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-class</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-mapping</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">test</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">url-pattern</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">/test/*</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">url-pattern</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-mapping</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">web-app</span><span style="color: #0000ff; font-family: Courier;">&gt;</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">该应用的部署路径为jetty-9.3.11/test-base/webapps/test.war。在该WAR文件所在的目录下，创建一个test.xml，其内容如下所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-family: Courier;">&lt;?</span><span style="color: #ff00ff; font-family: Courier;">xml&nbsp;version="1.0"&nbsp;&nbsp;encoding="ISO-8859-1"</span><span style="color: #0000ff; font-family: Courier;">?&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;!</span><span style="color: #ff00ff; font-family: Courier;">DOCTYPE&nbsp;Configure&nbsp;PUBLIC&nbsp;"-//Jetty//Configure//EN"&nbsp;"http://www.eclipse.org/jetty/configure_9_0.dtd"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">Configure&nbsp;</span><span style="color: #ff0000; font-family: Courier;">class</span><span style="color: #0000ff; font-family: Courier;">="org.eclipse.jetty.webapp.WebAppContext"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">Set&nbsp;</span><span style="color: #ff0000; font-family: Courier;">name</span><span style="color: #0000ff; font-family: Courier;">="contextPath"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">/</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">Set</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">Set&nbsp;</span><span style="color: #ff0000; font-family: Courier;">name</span><span style="color: #0000ff; font-family: Courier;">="war"</span><span style="color: #0000ff; font-family: Courier;">&gt;&lt;</span><span style="color: #800000; font-family: Courier;">SystemProperty&nbsp;</span><span style="color: #ff0000; font-family: Courier;">name</span><span style="color: #0000ff; font-family: Courier;">="jetty.base"</span><span style="color: #ff0000; font-family: Courier;">&nbsp;default</span><span style="color: #0000ff; font-family: Courier;">="."</span><span style="color: #0000ff; font-family: Courier;">/&gt;</span><span style="color: #000000; font-family: Courier;">/webapps/test.war</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">Set</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">Configure</span><span style="color: #0000ff; font-family: Courier;">&gt;</span></div><span style="font-size: 10pt;">启动Jetty服务器，使用默认的HTTP和HTTPS端口，分别为8080和8443。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;java&nbsp;-jar&nbsp;../start.jar<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:51.190:INFO:oejs.Server:main:&nbsp;jetty-9.3.11.v20160721<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:51.237:INFO:oejdp.ScanningAppProvider:main:&nbsp;Deployment&nbsp;monitor&nbsp;[file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/]&nbsp;at&nbsp;interval&nbsp;1<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.251:INFO:oejw.StandardDescriptorProcessor:main:&nbsp;NO&nbsp;JSP&nbsp;Support&nbsp;for&nbsp;/test.war,&nbsp;did&nbsp;not&nbsp;find&nbsp;org.eclipse.jetty.jsp.JettyJspServlet<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.313:INFO:oejsh.ContextHandler:main:&nbsp;Started&nbsp;o.e.j.w.WebAppContext@4520ebad{/test.war,file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/test.war/,AVAILABLE}{D:\http2\jetty\jetty-9.3.11\test-base\webapps\test.war}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.391:INFO:oejw.StandardDescriptorProcessor:main:&nbsp;NO&nbsp;JSP&nbsp;Support&nbsp;for&nbsp;/,&nbsp;did&nbsp;not&nbsp;find&nbsp;org.eclipse.jetty.jsp.JettyJspServlet<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.391:INFO:oejsh.ContextHandler:main:&nbsp;Started&nbsp;o.e.j.w.WebAppContext@711f39f9{/,file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/test.war/,AVAILABLE}{/test.war}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.532:INFO:oejs.AbstractConnector:main:&nbsp;Started&nbsp;ServerConnector@1b68ddbd{HTTP/1.1,[http/1.1,&nbsp;h2c,&nbsp;h2c-17,&nbsp;h2c-16,&nbsp;h2c-15,&nbsp;h2c-14]}{0.0.0.0:8080}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.735:INFO:oejus.SslContextFactory:main:&nbsp;x509=X509@e320068(jetty,h=[jetty.eclipse.org],w=[])&nbsp;for&nbsp;SslContextFactory@1f57539(file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore,file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore)<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.735:INFO:oejus.SslContextFactory:main:&nbsp;x509=X509@76f2b07d(mykey,h=[],w=[])&nbsp;for&nbsp;SslContextFactory@1f57539(file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore,file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore)<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:53.234:INFO:oejs.AbstractConnector:main:&nbsp;Started&nbsp;ServerConnector@4b168fa9{SSL,[ssl,&nbsp;alpn,&nbsp;h2,&nbsp;h2-17,&nbsp;h2-16,&nbsp;h2-15,&nbsp;h2-14,&nbsp;http/1.1]}{0.0.0.0:8443}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:53.249:INFO:oejs.Server:main:&nbsp;Started&nbsp;@3940ms</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">根据上述日志可知，Jetty启用了Web应用test.war，还启动了两个ServerConnector，一个支持h2c，另一个支持h2。值得注意的是，这两个ServerConnector还分别支持h2c-17, h2c-16, h2c-15, h2c-14和h2-17, h2-16, h2-15, h2-14。这是因为，HTTP/2在正式发布之前，先后发布了18个草案，其编号为00-17。所以，这里的h2c-XX和h2-XX指的就是第XX号草案。</span><br /><br /><strong><span style="font-size: 12pt;">3. 客户端</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 其实最方便的客户端就是浏览器了。只要使用的FireFox或Chrome版本不是太老，肯定都已经支持了HTTP/2，而且这一功能是默认打开的。也就是说，当使用FireFox去访问前面所部署的Web应用时，就是在使用HTTP/2，但你不会感觉到这种变化。使用FireFox提供的Developer Tools中的Network工具查看服务器端的响应，会发现HTTP版本为HTTP/2.0。但此处希望这个客户端能够提供更为丰富的与服务器端进行交互的功能，那么浏览器就并不合适了。<br />&nbsp;&nbsp;&nbsp; Jetty也实现了支持HTTP/2的客户端，但这个客户端是一个API，需要编写程序去访问HTTP/2服务器端。而且，目前该API的设计抽象层次较低，需要应用程序员对HTTP/2协议，比如各种帧，有较深入的了解。这对于初涉HTTP/2的开发者来说，显然很不合适。本文选择使用C语言编写的一个工具，其实也是HTTP/2的客户端实现之一，curl。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; curl在支持HTTP/2时，实际上是使用了nghttp2的C库，所以需要先安装nghttp2。另外，为了让curl支持h2，就必须要有TLS-ALPN的支持。那么，一般地还需要安装OpenSSL 1.0.2+。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 网络上关于在Linux下安装支持HTTP/2的curl的资源有很多，过程并不难，但有点儿繁，要安装的依赖比较多，本文就不赘述了。如果是使用Windows，笔者比较推荐通过Cygwin来安装和使用curl。在Windows中安装Cygwin非常简单，在Cygwin中执行各种命令时，感觉上就如同在使用Linux，尽管它并不是一个虚拟机。通过Cygwin安装curl，它会自动地安装所需的各种依赖程序和库。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在笔者的机器上，通过查看curl的版本会出现如下信息：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">curl&nbsp;</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; font-family: Courier;">&nbsp;(x86_64-unknown-cygwin)&nbsp;libcurl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; font-family: Courier;">&nbsp;OpenSSL/</span><span style="color: #000000; font-family: Courier;">1.0</span><span style="color: #000000; font-family: Courier;">.2h&nbsp;zlib/</span><span style="color: #000000; font-family: Courier;">1.2.8</span><span style="color: #000000; font-family: Courier;">&nbsp;libidn/</span><span style="color: #000000; font-family: Courier;">1.29</span><span style="color: #000000; font-family: Courier;">&nbsp;libpsl/</span><span style="color: #000000; font-family: Courier;">0.14.0</span><span style="color: #000000; font-family: Courier;">&nbsp;(+libidn/</span><span style="color: #000000; font-family: Courier;">1.29</span><span style="color: #000000; font-family: Courier;">)&nbsp;libssh2/</span><span style="color: #000000; font-family: Courier;">1.7.0</span><span style="color: #000000; font-family: Courier;">&nbsp;nghttp2/</span><span style="color: #000000; font-family: Courier;">1.14.0</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">Protocols:&nbsp;dict&nbsp;file&nbsp;ftp&nbsp;ftps&nbsp;gopher&nbsp;http&nbsp;https&nbsp;imap&nbsp;imaps&nbsp;ldap&nbsp;ldaps&nbsp;pop3&nbsp;pop3s&nbsp;rtsp&nbsp;scp&nbsp;sftp&nbsp;smb&nbsp;smbs&nbsp;smtp&nbsp;smtps&nbsp;telnet&nbsp;tftp<br /></span><span style="color: #000000; font-family: Courier;">Features:&nbsp;Debug&nbsp;IDN&nbsp;IPv6&nbsp;Largefile&nbsp;GSS-API&nbsp;Kerberos&nbsp;SPNEGO&nbsp;NTLM&nbsp;NTLM_WB&nbsp;SSL&nbsp;libz&nbsp;TLS-SRP&nbsp;HTTP2&nbsp;UnixSockets&nbsp;Metalink&nbsp;PSL</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">由上可知，笔者使用的curl版本是7.50.2，nghttp2版本是1.14.0，而OpenSSL版本是1.0.2h。</span><br /><br /><strong><span style="font-size: 12pt;">4. 第一次尝试</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在第一次尝试中，只需要简单地访问第2节中部署的Web应用中的静态文本文件index，以感受下h2c，完整命令如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;curl&nbsp;-v&nbsp;--http2&nbsp;http://localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; font-family: Courier;">/index</span></div><span style="font-size: 10pt;">在输出中包含有如下的内容：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">...<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;GET&nbsp;/index&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Host:&nbsp;localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;User-Agent:&nbsp;curl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Accept:&nbsp;*/*<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Connection:&nbsp;Upgrade</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;HTTP2-Settings<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Upgrade:&nbsp;h2c<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;HTTP2-Settings:&nbsp;AAMAAABkAAQAAP__<br /></span><span style="color: #000000; font-family: Courier;">&gt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; font-family: Courier;">&nbsp;Switching&nbsp;Protocols<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Received&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Using&nbsp;HTTP2</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;server&nbsp;supports&nbsp;multi-use<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Connection&nbsp;state&nbsp;changed&nbsp;(HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;confirmed)<br />...<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">200</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;server:&nbsp;Jetty(</span><span style="color: #000000; font-family: Courier;">9.3.11</span><span style="color: #000000; font-family: Courier;">.v20160721)<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;last-modified:&nbsp;Wed</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">14</span><span style="color: #000000; font-family: Courier;">&nbsp;Sep&nbsp;</span><span style="color: #000000; font-family: Courier;">2016</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">12</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">52</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">32</span><span style="color: #000000; font-family: Courier;">&nbsp;GMT<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;content-length:&nbsp;</span><span style="color: #000000; font-family: Courier;">11</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;accept-ranges:&nbsp;bytes<br /></span><span style="color: #000000; font-family: Courier;">&lt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;Test</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">"&gt;"是客户端发送的请求，"&lt;"是服务器端发送的响应，而"*"是curl对当前过程的说明。</span><span style="font-size: 10pt;">结合本系列<a href="http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html">第一篇文章</a>中所简述的HTTP 2协议，可以有以下的基本理解。</span><br /><span style="font-size: 10pt;">[1]客户端发起了一个HTTP/1.1的请求，其中携带有Upgrade头部，要求服务器端升级到HTTP/2(h2c)。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">&gt;&nbsp;GET&nbsp;/index&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Host:&nbsp;localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;User-Agent:&nbsp;curl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Accept:&nbsp;*/*<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Connection:&nbsp;Upgrade</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;HTTP2-Settings<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Upgrade:&nbsp;h2c<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;HTTP2-Settings:&nbsp;AAMAAABkAAQAAP__<br /></span><span style="color: #000000; font-family: Courier;">&gt;</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">[2]服务器端同意升级，返回响应"101 Switching Protocols"，然后客户端收到了101响应，HTTP/2连接进行确认。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; font-family: Courier;">&nbsp;Switching&nbsp;Protocols<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Received&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Using&nbsp;HTTP2</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;server&nbsp;supports&nbsp;multi-use<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Connection&nbsp;state&nbsp;changed&nbsp;(HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;confirmed)</span></div><span style="font-size: 10pt;">[3]服务器端响应最终结果。状态行中出现的HTTP版本为HTTP/2，状态代码为200，且后面没有跟着"OK"。最后输出了index文件的内容"HTTP/2 Test"。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">200</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;server:&nbsp;Jetty(</span><span style="color: #000000; font-family: Courier;">9.3.11</span><span style="color: #000000; font-family: Courier;">.v20160721)<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;last-modified:&nbsp;Wed</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">14</span><span style="color: #000000; font-family: Courier;">&nbsp;Sep&nbsp;</span><span style="color: #000000; font-family: Courier;">2016</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">12</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">52</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">32</span><span style="color: #000000; font-family: Courier;">&nbsp;GMT<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;content-length:&nbsp;</span><span style="color: #000000; font-family: Courier;">11</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;accept-ranges:&nbsp;bytes<br /></span><span style="color: #000000; font-family: Courier;">&lt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;Test</span></div><span style="font-size: 10pt;"></span><br /><strong><span style="font-size: 12pt;">5. 一个局限</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这次，在发起的请求中包含体部，命令如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;curl&nbsp;-v&nbsp;--http2&nbsp;-d&nbsp;</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">body</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">&nbsp;http://localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; font-family: Courier;">/index</span></div><span style="font-size: 10pt;">在输出中包含有如下的内容：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">...<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;POST&nbsp;/index&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Host:&nbsp;localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;User-Agent:&nbsp;curl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Accept:&nbsp;*/*<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Connection:&nbsp;Upgrade</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;HTTP2-Settings<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Upgrade:&nbsp;h2c<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;HTTP2-Settings:&nbsp;AAMAAABkAAQAAP__<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Content-Length:&nbsp;</span><span style="color: #000000; font-family: Courier;">4</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Content-Type:&nbsp;application/x-www-form-urlencoded<br /></span><span style="color: #000000; font-family: Courier;">&gt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">200</span><span style="color: #000000; font-family: Courier;">&nbsp;OK<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;Last-Modified:&nbsp;Wed</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">14</span><span style="color: #000000; font-family: Courier;">&nbsp;Sep&nbsp;</span><span style="color: #000000; font-family: Courier;">2016</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">12</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">52</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">32</span><span style="color: #000000; font-family: Courier;">&nbsp;GMT<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;Accept-Ranges:&nbsp;bytes<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;Content-Length:&nbsp;</span><span style="color: #000000; font-family: Courier;">11</span><span style="color: #000000; "><br />...<br /></span><span style="color: #000000; font-family: Courier;">HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;Test</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">和第4节中的输出进行比较，会发现缺少了"101 Switching Protocols"那一段，而且最终响应状态行中出现的HTTP版本是HTTP/1.1。这就说明服务器端不同意升级，后面继续使用HTTP/1.1。刚刚部署的Jetty未做任何改变怎么会突然不支持HTTP/2了呢？或者这是curl的问题？其实，</span><span style="font-size: 10pt;">这是因为Jetty服务器端在实现h2c时不支持请求中包含体部。另外，Apache httpd也有同样的问题。如果是使用h2，则没有这个限制。这背后的原因超出了本文的范畴，不作表述。</span><br /><br /><strong><span style="font-size: 12pt;">6. 一个Bug</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在这次尝试中，测试一下两端对100-continue的支持。如果请求中使用了头部"Expect: 100-continue"，那么正常地该请求要有体部。但由于在第5节中介绍的问题，此时不能再使用h2c，而只能使用h2。另外，这次不访问静态文件，而是访问Servlet(此处为/test)。完整命令如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;curl&nbsp;-vk&nbsp;--http2&nbsp;-H&nbsp;</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">Expect:&nbsp;100-continue</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">&nbsp;-d&nbsp;</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">body</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">&nbsp;https://localhost:</span><span style="color: #000000; font-family: Courier;">8443</span><span style="color: #000000; font-family: Courier;">/test</span></div><span style="font-size: 10pt;">在输出的最后出现了如下信息：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">curl:&nbsp;(</span><span style="color: #000000; font-family: Courier;">92</span><span style="color: #000000; font-family: Courier;">)&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;stream&nbsp;</span><span style="color: #000000; font-family: Courier;">1</span><span style="color: #000000; font-family: Courier;">&nbsp;was&nbsp;not&nbsp;closed&nbsp;cleanly:&nbsp;CANCEL&nbsp;(err&nbsp;</span><span style="color: #000000; font-family: Courier;">8</span><span style="color: #000000; font-family: Courier;">)</span></div><span style="font-size: 10pt;">这其实是Jetty的一个<a href="https://github.com/eclipse/jetty.project/issues/902">Bug</a>，正在开发中的9.3.12已经修复了它。</span><br /><br /><strong><span style="font-size: 12pt;">7. 小结</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; HTTP/2依然算是新潮的技术，对各家的实现，无论是服务器端，客户端，还是分析工具，都要持有一份怀疑态度。这些实现和工具都是程序，都有可能存在bug。而且协议对许多细节没有作出规定，各家都会发挥自己的想像力。比如，Apache httpd和Jetty在实现服务器端推送时，其方式就不尽相同。<br />&nbsp;&nbsp;&nbsp; 在开发自己的HTTP/2实现或应用的时候，需要同时使用已有的不同服务器端和客户端去部署多套测试环境进行对比分析。<br /></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431814.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-09-20 16:42 <a href="http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索HTTP/2: HTTP 2协议简述(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 19 Sep 2016 03:36:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431811.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431811.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431811.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: HTTP/2协议简述</span></strong></div>
<span style="font-size: 10pt;">HTTP/2的协议包含着两个RFC：Hypertext Transfer Protocol Version 2 (<a href="https://tools.ietf.org/html/rfc7540">RFC7540</a>)，即HTTP/2；HPACK: Header Compression for HTTP/2 (<a href="https://tools.ietf.org/html/rfc7541">RFC7541</a>)，即HPACK。RFC7540描述了HTTP/2的语义，RFC7541则描述了用于HTTP/2的头部压缩的格式。本文只涉及HTTP/2协议，本系列的后续文章将会涉及HPACK协议。(2016.10.13最后更新)</span><br />
<br />
<strong><span style="font-size: 12pt;">1. HTTP/2要解决的问题</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/1.0只允许在一个TCP连接中出现一个请求。后来的HTTP/1.1虽然引入了请求流水线，以允许在一个连接中发送多个请求，但这只是部分地解决了请求并发的问题。服务器端在返回响应时，还是必须要按照它接收到的请求的顺序进行返回。如果排在前面的响应要消耗较长的时间，那依然会对后面的响应的造成阻塞，亦即线头阻塞(Head-of-line blocking)。所以，客户端必须要使用多条连接去发起多个的请求以实现并发，并进而减小延迟。更大的并发会增大服务器的负载，也会占用更大的网络带宽。另外，头部通常会包含有大量的信息，如cookie，而这也会增加网络传输的开销。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2允许在同一个TCP连接中交错地出现多个请求与响应，亦即多工(Multiplex)。同时，它使用了一个高效的编码方法对头部进行压缩。HTTP/2还允许对请求进行优先级排序，以便让更为重要的请求得以更快的完成，这会进一步提高性能。HTTP/2还改变了服务器端只能被动地向客户端返回响应的定式，允许服务器端主动地向客户端推送数据，这就可以减少客户端发起请求的数量。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">总之，HTTP/2主要是解决性能问题。</span><br />
<br />
<strong><span style="font-size: 12pt;">2. 发起HTTP/2</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2会使用与HTTP/1相同的URI scheme，即http和https。而且实现HTTP/2的服务器端也不会使用不同的端口去分别支持HTTP/1和HTTP/2。这样有利于平滑地从HTTP/1升级到HTTP/2。毕竟目前已部署的绝大部分网络服务都只支持HTTP/1，当未来它们升级到HTTP/2时，如果换用了不同URI scheme或端口，那么肯定会对客户端产生极大的影响。但是HTTP/2协议为运行在http和https上的HTTP/2分别定义了两个不同的标识符：h2c和h2。h2c中的"c"指的是cleartext，即明文。本文后面会使用h2c指代运行在http2上(直接使用TCP)的HTTP/2，而用h2指代运行在https上(使用TLS)的HTTP/2。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">那么，支持HTTP/2的客户端如何知道它所连接的服务器端是否也支持HTTP/2呢？</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">对于h2c，支持HTTP/2的客户端可以在发起的请求中使用HTTP/1.1的Upgrade头部去尝试要求服务器升级到HTTP/2。该请求的格式如下：</span><br />
<span style="font-size: 10pt;">GET / HTTP/1.1</span><br />
<span style="font-size: 10pt;">Host: server.example.com</span><br />
<span style="font-size: 10pt;">Connection: Upgrade, HTTP2-Settings</span><br />
<span style="font-size: 10pt;">Upgrade: h2c</span><br />
<span style="font-size: 10pt;">HTTP2-Settings: &lt;base64url encoding of HTTP/2 SETTINGS payload&gt;</span><br />
<span style="font-size: 10pt;">HTTP2-Settings是一个经由BASE64编码过的字符串，其原始内容是客户端将要发送的SETTINGS帧的载荷，即一些配置参数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">如果服务器端支持HTTP/2，它就响应"101 Switching Protocols"，表示可以进行升级。该响应的格式如下：</span><br />
<span style="font-size: 10pt;">HTTP/1.1 101 Switching Protocols</span><br />
<span style="font-size: 10pt;">Connection: Upgrade</span><br />
<span style="font-size: 10pt;">Upgrade: h2c</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">如果服务器端不支持HTTP/2，则会忽略Upgrade请求头部，后续依然使用HTTP/1.1。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">对于h2，会使用到协议Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension (<a href="https://tools.ietf.org/html/rfc7301">RFC7301</a>)，即TLS-ALPN。该协议允许客户端和服务器端就使用何种版本的HTTP进行协商。如果TLS-ALPN在现实中运行良好的话，也许某天还会使用该方法去协商使用别的协议。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">当客户端与服务器端都同意使用HTTP/2时，双方都需要各自发出一个连接序言(Connection Preface)以进行最后的确认。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">客户端在接收到服务器端的"101 Switching Protocols"响应(针对h2c)或TLS连接的第一个应用数据字节(针对h2)之后会立即发出连接序言。该序言的开头是"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"(其十六进制形式为"0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a")<sup>(1)</sup>，后面必须再跟一个SETTINGS帧，哪怕这个帧是空的。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">服务器端的连接序言则由一个SETTINGS帧构成，该帧必须是服务器端在HTTP/2连接中发送的第一个帧。这个SETTINGS帧可以为空，也可以包含一些希望客户端如何与自己进行通信的必要配置信息。</span><br />
<br />
<strong><span style="font-size: 12pt;">3. 帧(Frame)</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2消息使用二进制格式(实际编码时使用十六进制书写)，相比于文本格式，这样可以提高消息处理的效率。HTTP/2消息的最小单元为帧，它由头部与载荷(Payload)组成。每个帧的长度必须是一个或多个8比特位字节(octet，下文将其简写为"字节")。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">帧头部依次包含有如下的5个字段：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">长度(Length)：该字段占用24个比特位，代表帧载荷的长度。该长度是一个24位的无符号整数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">类型(Type)：该字段占用8个比特位，代表帧的类型。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">标志(Flags)：该字段占用8个比特位，代表帧所定义的一个或多个标志。并不是所有的帧都定义了标志。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">保留位(R)：该字段占用1个比特位，其语义尚未被定义。在读取帧时，该位需要被忽略；但在发送帧时，该位需要保持为0(0x0)。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">流标识符(Stream Identifier)：该字段占用31个比特位，代表该帧所在流的标识符。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在头部之后，紧接着的就是载荷。载荷的结构与内容完全由帧的类型决定，它的长度也是不定的。<br />
<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2定义了如下10种不同类型的帧。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">DATA：用于携带一组长度不定的字节。一个或多个DATA可作为请求或响应的载荷。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HEADERS：用于开启一个流，并可携带一个头部块片断。头部块指由一个HEADERS/PUSH_PROMISE帧和紧随它的零到多个CONTINUATION帧组成的集合，因为只有它们才可能携带头部信息。这个集合可被分割成一个或一组字节，这样的字节被称为头部块片断。头部块中各个特定类型的帧必须紧紧相邻，不能出现其它类型的帧。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">PRIORITY：用于指定发送端建议的流优先级。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">RST_STREAM：用于立即终止流。当希望取消一个流或发生错误时，就可发送RST_STREAM帧。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">SETTINGS：用于携带可以影响两端之间通信方式的配置参数。SETTINGS帧定义了一个ACK标志，用于指示该帧所设置的参数是否已被接收端获知。当收到一个SETTINGS且其中的ACK标志为0时，接收端必须尽可能快的应用其中已被更新的参数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">PUSH_PROMISE：用于向接收端通知发送端将要创建的流。当接收端接收到该帧时，新的流尚未被发送端创建，但发送端承诺会创建该流。该帧用于实现HTTP/2的重要特性"服务器端推送(Server Push)"。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">PING：用于测量发送端与接收端之间的最小往返时间。这与使用众所周知的ping命令的目的相似，是为了测试某个空闲的连接是否还可用。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">GOAWAY：用于发起对连接的关闭，或触发严重的错误条件。该帧允许一端，在完成对之前已创建的流的处理的同时，优雅地停止接收新的流。一端在创建新的流，另一端在发送GOAWAY，这两者之间天然存在着竞争关系。为了就对这种情况，发送端在发送GOAWAY时会让它携带上(该发送端所知晓的)接收端最后创建的流的标识符，当该GOAWAY被发送之后，发送端将会忽视由接收端创建的任何一个标识符比该标识符大的流。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">WINDOW_UPDATE：用于流量控制。该帧的载荷由一个单比特保留位和一个31比特位的无符号整数组成。该整数向该帧的接收端指示了其向当前流量控制窗口所能增加传输量的值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">CONTINUATION：用于继续发送头部块片断。只要同一个流中前面的帧是HEADERS，PUSH_PROMISE或CONTINUATION，并且该帧没有设置END_HEADERS标志，那么可无限量地发送CONTINUATION帧。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp; 部分帧，DATA，HEADERS和PUSH_PROMISE，的载荷中可能包含填白(Padding)。填白在业务上没有实际的用处，它的出现是基于安全目的。比如，可以用它来扰乱实际数据的长度，以减轻特定的HTTP攻击。<br />
<br />
</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp; 发送端发送的帧的最大长度要尊重接收端设定的SETTINGS_MAX_FRAME_SIZE的值。但该值的范围要介于2^14至2^24-1个字节之间。</span><br />
<br />
<strong><span style="font-size: 12pt;">4. 流(Stream)</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">流是用于在客户端与服务器端之间进行帧传送的通道，同一个TCP连接中可以同时有多个流，如下图所示，<br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;"></span>
<div><span style="font-family: Courier;"></span><span style="font-family: Courier;">
<div>
<div>
<div>&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connection&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474; ============================= &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; --------------------- &lt;-- Stream&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&#9484;&#9472;&#9488;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&#9492;&#9472;&#9496; &lt;-- Frame&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; ---------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474; Client &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474; Server &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; ----------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9484;&#9472;&#9472;&#9488;&#9484;&#9472;&#9472;&#9472;&#9472;&#9488;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472;&#9496;&#9492;&#9472;&#9472;&#9472;&#9472;&#9496;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; ----------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474; ============================= &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</div>
</div>
</div>
</span></div>
</div>
</div>
<span style="font-size: 10pt;">服务器端和客户端可以交错地向同一个连接中的不同流中传送帧。可以把一个流看作HTTP/1中的一个连接。客户端与服务器端在同一个流中的交互依然遵循发送请求-等待响应模式。两端都可以创建新的流，共享对方创建的流，也可以关闭对方创建的流。帧在流中的顺序是有意义的，接收端会以接收到的顺序去处理帧。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 每个流都有一个标识符，是一个31比特位的无符合整数。在同一个连接中，流标识符是唯一的。由客户端创建的流的标识符为奇数，由服务器创建的流的标识符为偶数。但标识符为0的流可看作连接，用于连接控制信息，创建新的流时不可使用该标识符。同一个连接中的任何一个流的标识符都不可重用，即便这个流已被关闭了。对于长时间没有中断的连接，可能会出现标识符不够用的情况，那时就必须强制创建一个新的连接。<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2协议为流的生命周期定义了7种状态<sup>(2)</sup>：idle，reserved(local)，reserved(remote)，open，half closed(local)，half closed(remote)和closed。当一端接收或发送头部块或(帧DATA和HEADERS的)标志RST_STREAM后可使流的状态发生转变。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">使用流来实现多工就会引起对TCP连接使用的竞争，这会造成流的阻塞。基于帧WINDOW_UPDATE的流量控制方案可以确保相同连接中的流相互之间不会产生破坏性干扰。流量控制可以作用于两个层面，即单个流或整个连接。只有帧DATA需要遵守流量控制，所有其它的帧所有消耗的空间均不会占用流量控制窗口。HTTP/2协议只是定义了WINDOW_UPDATE帧的结构和语义，协议的实现可以选择任何适用自己的流量控制算法。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">流可以有优先级。客户端在创建一个新的流时，可在HEADERS中指定优先级权重。在后续任何时间，通过PRIORITY可以改变流的优先级权重。在并发能力有限的情况下，高权重流的帧会被优先传送。权重的值必须介于1至256之间，默认权重为16。</span><span style="font-size: 10pt;">流与流之间还可以有依赖关系，这种关系会组成一棵依赖关系树。一个流能够指定自己成为另一个流的子流。这一过程，可以是非排他的，也可以是排他的。非排他性依赖，是指一个流在将自己变成另一个流的子流的过程中，允许另一个流还有别的子流，即允许有自己的兄弟流存在。排他性依赖，指在前述过程中，不允许另一个流还有别的子流。如果另一个流已经有子流了，那么该流会把所有潜在的兄弟流先变成自己的子流，然后再使自己成为另一个流的唯一子流。其实，排他性依赖的作用就是为了能够打破已有的关系树，在既成的父子节点中插入新的节点。否则，只能为已有节点添加子节点，那么关系树将不可能进行重构。所有的流在被创建时，默认成为标识符为0x0的流的子流。在"服务器端推送"中生成的"推送"流将自动地成为生成该推送流的流的子流，其默认权重也为16。</span><br />
<br />
<strong><span style="font-size: 12pt;">5. 消息交换</span></strong><br />
<strong><span style="font-size: 10pt;">5.1 请求/响应交换</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2沿袭了HTTP/1的语义，即所有的请求与响应语义均得到了保留，尽管传递这些语义的语法已经改变了。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">一个HTTP/2消息由如下几个部分组成：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[1]仅对于响应消息，可以包含一个携带有1xx响应头部的头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[2]一个头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[3]零到多个携带有体部(Body)消息的DATA帧。HTTP/1中使用的"分块(chunked)"体部将不适用于HTTP/2。因为一个体部可由多个DATA帧组成，所以HTTP/2的体部天然就是可分块的。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[4]一个可能存在的包含着尾部消息的头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。<br />
<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">HTTP/2仍然沿用HTTP/1中的头部字段，但字段名称中的字母必须全部为小写。另外，还将HTTP/1消息开始行(请求中的<a href="https://tools.ietf.org/html/rfc2616#section-5.1">请求行</a>与响应中的<a href="https://tools.ietf.org/html/rfc2616#section-6.1">状态行</a>)中的消息，分解成了若干伪头部字段，此类字段均以冒号(:)开头。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">HTTP/1请求行格式为"method request-target HTTP-version"，对应的HTTP/2伪头部字段有:method=method和:path=request-target，但HTTP-version无对应字段，默认为HTTP/2。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">HTTP/1状态行格式为"HTTP-version status-code reason-phrase"，对应的HTTP/2伪头部字段有:status=status-code。但HTTP-version无对应字段，默认为HTTP/2；reason-phrase也无对应字段，因为可以通过状态代码查找到其对应的reason-phrase。HTTP/2协议是在尽量减少冗余消息。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2协议还为请求头部定义了另外两个伪字段：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">:scheme：URI中的scheme部分。它可以不仅仅是http或https，因为有时候可能会与非HTTP服务进行交互。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">:authority：URI中的授权部分。即，scheme://user:password@host:port/path?query#fragment中的"user:password@host:port"。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2协议<a href="https://tools.ietf.org/html/rfc7540#section-8.1.3">8.1.3</a>节中给出一些简单示例，展示了如何将HTTP/1消息对应到HTTP/2消息。</span><br />
<strong><span style="font-size: 10pt;">5.2 服务器端推送</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2的服务器端推送是传统的请求/响应模式的一种特殊形式。服务器端在收到客户端的请求(主请求)之后，为了主动向客户端推送更多的内容，会自动地生成若干新的请求(推送请求)。服务器向客户端发送的响应中，不仅包含对主请求的响应(主响应)，还包含对推送请求的响应(推送响应)。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">客户端可以通过发送包含有SETTINGS_MAX_CONCURRENT_STREAMS参数的SETTINGS帧去禁用服务器端推送，也可以通过发送RST_STREAM帧去取消已经发起的服务器端推送，但不能发送包含有END_STREAM标志的帧。<br />
<br />
(1)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"中的"PRI"与"SM"合起来就是"RRISM(棱镜)"。呵呵，HTTPbis工作组这是想表达什么意思呢 ;-)<br />
(2)本系列的<a href="http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html">后续文章</a>解读了流的状态。<br />
</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-09-19 11:36 <a href="http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Play OpenJDK: 允许你的包名以"java."开头(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2015/11/01/428010.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 01 Nov 2015 12:06:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2015/11/01/428010.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/428010.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2015/11/01/428010.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/428010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/428010.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Play OpenJDK: 允许你的包名以"java."开头</span></strong></div><br />本文是Play OpenJDK的第二篇，介绍了如何突破JDK不允许自定义的包名以"java."开头这一限制。这一技巧对于基于已有的JDK向java.*中添加新类还是有所帮助的。(2015.11.02最后更新)<br /><br />无论是经验丰富的Java程序员，还是Java的初学者，总会有一些人或有意或无意地创建一个包名为"java"的类。但出于安全方面的考虑，JDK不允许应用程序类的包名以"java"开头，即不允许java，java.foo这样的包名。但javax，javaex这样的包名是允许的。<br /><br /><strong><span style="font-size: 12pt;">1. 例子</span></strong><br />比如，以OpenJDK 8为基础，臆造这样一个例子。笔者想向OpenJDK贡献一个同步的HashMap，即类SynchronizedHashMap，而该类的包名就为java.util。SynchronizedHashMap是HashMap的同步代理，由于这两个类是在同一包内，SynchronizedHashMap不仅可以访问HashMap的public方法与变量，还可以访问HashMap的protected和default方法与变量。SynchronizedHashMap看起来可能像下面这样：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;java.util;<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;SynchronizedHashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">K,&nbsp;V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">K,&nbsp;V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;hashMap&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;SynchronizedHashMap(HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">K,&nbsp;V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;hashMap)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.hashMap&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;hashMap;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;SynchronizedHashMap()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;HashMap</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">());<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;V&nbsp;put(K&nbsp;key,&nbsp;V&nbsp;value)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;hashMap.put(key,&nbsp;value);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;V&nbsp;get(K&nbsp;key)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;hashMap.get(key);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;V&nbsp;remove(K&nbsp;key)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;hashMap.remove(key);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;size()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;hashMap.size;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;直接调用HashMap.size变量，而非HashMap.size()方法</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br /><strong><span style="font-size: 12pt;">2. ClassLoader的限制</span></strong><br />使用javac去编译源文件SynchronizedHashMap.java并没有问题，但在使用编译后的SynchronizedHashMap.class时，JDK的ClassLoader则会拒绝加载java.util.SynchronizedHashMap。<br />设想有如下的应用程序：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.util.SynchronizedHashMap;<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;SyncMapTest&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SynchronizedHashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;syncMap&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SynchronizedHashMap</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;syncMap.put(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Key</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Value</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(syncMap.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Key</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>使用java命令去运行该应用时，会报如下错误：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Exception&nbsp;in&nbsp;thread&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">main</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;java.lang.SecurityException:&nbsp;Prohibited&nbsp;package&nbsp;name:&nbsp;java.util<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.preDefineClass(ClassLoader.java:</span><span style="color: #000000; ">659</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.defineClass(ClassLoader.java:</span><span style="color: #000000; ">758</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.security.SecureClassLoader.defineClass(SecureClassLoader.java:</span><span style="color: #000000; ">142</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader.defineClass(URLClassLoader.java:</span><span style="color: #000000; ">467</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader.access$</span><span style="color: #000000; ">100</span><span style="color: #000000; ">(URLClassLoader.java:</span><span style="color: #000000; ">73</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(URLClassLoader.java:</span><span style="color: #000000; ">368</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(URLClassLoader.java:</span><span style="color: #000000; ">362</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.security.AccessController.doPrivileged(Native&nbsp;Method)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader.findClass(URLClassLoader.java:</span><span style="color: #000000; ">361</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.loadClass(ClassLoader.java:</span><span style="color: #000000; ">424</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:</span><span style="color: #000000; ">331</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.loadClass(ClassLoader.java:</span><span style="color: #000000; ">357</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;SyncMapTest.main(SyncMapTest.java:</span><span style="color: #000000; ">6</span><span style="color: #000000; ">)</span></div>方法ClassLoader.preDefineClass()的源代码如下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;ProtectionDomain&nbsp;preDefineClass(String&nbsp;name,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ProtectionDomain&nbsp;pd)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">checkName(name))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;NoClassDefFoundError(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">IllegalName:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;name);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;((name&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;name.startsWith(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">java.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SecurityException<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Prohibited&nbsp;package&nbsp;name:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name.substring(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;name.lastIndexOf(</span><span style="color: #000000; ">'</span><span style="color: #000000; ">.</span><span style="color: #000000; ">'</span><span style="color: #000000; ">)));<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(pd&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pd&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;defaultDomain;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(name&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;checkCerts(name,&nbsp;pd.getCodeSource());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;pd;<br />}</span></div>很清楚地，该方法会先检查待加载的类全名(即包名+类名)是否以"java."开头，如是，则抛出SecurityException。那么可以尝试修改该方法的源代码，以突破这一限制。<br />从JDK中的src.zip中拿出java/lang/ClassLoader.java文件，修改其中的preDefineClass方法以去除相关限制。重新编译ClassLoader.java，将生成的ClassLoader.class，ClassLoader$1.class，ClassLoader$2.class，ClassLoader$3.class，ClassLoader$NativeLibrary.class，ClassLoader$ParallelLoaders.class和SystemClassLoaderAction.class去替换JDK/jre/lib/rt.jar中对应的类。<br />再次运行SyncMapTest，却仍然会抛出相同的SecurityException，如下所示：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Exception&nbsp;in&nbsp;thread&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">main</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;java.lang.SecurityException:&nbsp;Prohibited&nbsp;package&nbsp;name:&nbsp;java.util<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.defineClass1(Native&nbsp;Method)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.defineClass(ClassLoader.java:</span><span style="color: #000000; ">760</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.security.SecureClassLoader.defineClass(SecureClassLoader.java:</span><span style="color: #000000; ">142</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader.defineClass(URLClassLoader.java:</span><span style="color: #000000; ">467</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader.access$</span><span style="color: #000000; ">100</span><span style="color: #000000; ">(URLClassLoader.java:</span><span style="color: #000000; ">73</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(URLClassLoader.java:</span><span style="color: #000000; ">368</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(URLClassLoader.java:</span><span style="color: #000000; ">362</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.security.AccessController.doPrivileged(Native&nbsp;Method)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.net.URLClassLoader.findClass(URLClassLoader.java:</span><span style="color: #000000; ">361</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.loadClass(ClassLoader.java:</span><span style="color: #000000; ">424</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:</span><span style="color: #000000; ">331</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ClassLoader.loadClass(ClassLoader.java:</span><span style="color: #000000; ">357</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;SyncMapTest.main(SyncMapTest.java:</span><span style="color: #000000; ">6</span><span style="color: #000000; ">)</span></div>此时是由方法ClassLoader.defineClass1()抛出的SecurityException。但这是一个native方法，那么仅通过修改Java代码是无法解决这个问题的(JDK真是层层设防啊)。原来在Hotspot的C++源文件hotspot/src/share/vm/classfile/systemDictionary.cpp中有如下语句：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">const&nbsp;char*&nbsp;pkg&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">java/</span><span style="color: #000000; ">"</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">if&nbsp;(!HAS_PENDING_EXCEPTION&nbsp;&amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;!class_loader.is_null()&nbsp;&amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;parsed_name&nbsp;!</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;NULL&nbsp;&amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;!strncmp((const&nbsp;char*)parsed_name-&gt;bytes()</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;pkg</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;strlen(pkg)))&nbsp;{<br />&nbsp;&nbsp;//&nbsp;It&nbsp;is&nbsp;illegal&nbsp;to&nbsp;define&nbsp;classes&nbsp;in&nbsp;the&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">java.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;package&nbsp;from<br />&nbsp;&nbsp;//&nbsp;JVM_DefineClass&nbsp;or&nbsp;jni_DefineClass&nbsp;unless&nbsp;you're&nbsp;the&nbsp;bootclassloader<br />&nbsp;&nbsp;ResourceMark&nbsp;rm(THREAD)</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;char*&nbsp;name&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parsed_name-&gt;as_C_string()</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;char*&nbsp;index&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;strrchr(name</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;'/')</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;*index&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;'\</span><span style="color: #000000; ">0</span><span style="color: #000000; ">'</span><span style="color: #008000; ">;</span><span style="color: #008000; ">&nbsp;//&nbsp;chop&nbsp;to&nbsp;just&nbsp;the&nbsp;package&nbsp;name</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;while&nbsp;((index&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;strchr(name</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;'/'))&nbsp;!</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;NULL)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;*index&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;'.'</span><span style="color: #008000; ">;</span><span style="color: #008000; ">&nbsp;//&nbsp;replace&nbsp;'/'&nbsp;with&nbsp;'.'&nbsp;in&nbsp;package&nbsp;name</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;}<br />&nbsp;&nbsp;const&nbsp;char*&nbsp;fmt&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Prohibited&nbsp;package&nbsp;name:&nbsp;%s</span><span style="color: #000000; ">"</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;size_t&nbsp;len&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;strlen(fmt)&nbsp;+&nbsp;strlen(name)</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;char*&nbsp;message&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;NEW_RESOURCE_ARRAY(char</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;len)</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;jio_snprintf(message</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;len</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;fmt</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;name)</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">&nbsp;&nbsp;Exceptions::_throw_msg(THREAD_AND_LOCATION</span><span style="color: #000000; ">,</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;vmSymbols::java_lang_SecurityException()</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;message)</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; ">}</span></div>修改该文件以去除掉相关限制，并按照本系列的<a href="http://www.blogjava.net/jiangshachina/archive/2015/10/30/427994.html">第一篇文章</a>中介绍的方法去重新构建一个OpenJDK。那么，这个新的JDK将不会再对包名有任何限制了。<br /><br /><div><strong style="font-size: 12pt;">3. 覆盖Java核心API？</strong><br />开发者们在使用主流IDE时会发现，如果工程有多个jar文件或源文件目录中包含相同的类，这些IDE会根据用户指定的优先级顺序来加载这些类。比如，在Eclipse中，右键点击某个Java工程--&gt;属性--&gt;Java Build Path--&gt;Order and Export，在这里调整各个类库或源文件目录的位置，即可指定加载类的优先级。<br />当开发者在使用某个开源类库(jar文件)时，想对其中某个类进行修改，那么就可以将该类的源代码复制出来，并在Java工程中创建一个同名类，然后指定Eclipse优先加息自己创建的类。即，在编译时与运行时用自己创建的类去覆盖类库中的同名类。那么，是否可以如法炮制去覆盖Java核心API中的类呢？<br />考虑去覆盖类java.util.HashMap，只是简单在它的put()方法添加一条打印语。那么就需要将src.zip中的java/util/HashMap.java复制出来，并在当前Java工程中创建一个同名类java.util.HashMap，并修改put()方法，如下所示：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;java.util;<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">K,V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;AbstractMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">K,V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">K,V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">,&nbsp;Cloneable,&nbsp;Serializable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" />.<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;V&nbsp;put(K&nbsp;key,&nbsp;V&nbsp;value)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">put&nbsp;-&nbsp;key=%s,&nbsp;value=%s%n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;key,&nbsp;value);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;putVal(hash(key),&nbsp;key,&nbsp;value,&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div>此时，在Eclipse环境中，SynchronizedHashMap使用的java.util.HashMap被认为是上述新创建的HashMap类。那么运行应用程序SyncMapTest后的期望输出应该如下所示：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">put&nbsp;-&nbsp;key</span><span style="color: #000000; ">=</span><span style="color: #000000; ">Key</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;value</span><span style="color: #000000; ">=</span><span style="color: #000000; ">Value<br />Value</span></div>但运行SyncMapTest后的实际输出却为如下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Value</span></div>看起来，新创建的java.util.HashMap并没有被使用上。这是为什么呢？能够"想像"到的原因还是类加载器。关于Java类加载器的讨论超出了本文的范围，而且关于该主题的文章已是汗牛充栋，但本文仍会简述其要点。<br />Java类加载器由下至上分为三个层次：引导类加载器(Bootstrap Class Loader)，扩展类加载器(Extension Class Loader)和应用程序类加载器(Application Class Loader)。其中引导类加载器用于加载rt.jar这样的核心类库。并且引导类加载器为扩展类加载器的父加载器，而扩展类加载器又为应用程序类加载器的父加载器。同时JVM在加载类时实行委托模式。即，当前类加载器在加载类时，会首先委托自己的父加载器去进行加载。如果父加载器已经加载了某个类，那么子加载器将不会再次加载。<br />由上可知，当应用程序试图加载java.util.Map时，它会首先逐级向上委托父加载器去加载该类，直到引导类加载器加载到rt.jar中的java.util.HashMap。由于该类已经被加载了，我们自己创建的java.util.HashMap就不会被重复加载。<br />使用java命令运行SyncMapTest程序时加上VM参数-verbose:class，会在窗口中打印出形式如下的语句：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Opened&nbsp;/home/ubuntu/jdk1.8.0_custom/jre/lib/rt.jar</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br /></span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Loaded&nbsp;java.lang.Object&nbsp;from&nbsp;/home/ubuntu/jdk1.8.0_custom/jre/lib/rt.jar</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br /><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /></span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Loaded&nbsp;java.util.HashMap&nbsp;from&nbsp;/home/ubuntu/jdk1.8.0_custom/jre/lib/rt.jar</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br /></span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Loaded&nbsp;java.util.HashMap$Node&nbsp;from&nbsp;/home/ubuntu/jdk1.8.0_custom/jre/lib/rt.jar</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br /><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /></span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Loaded&nbsp;java.util.SynchronizedHashMap&nbsp;from&nbsp;file:/home/ubuntu/projects/test/classes/</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />Value<br /></span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Loaded&nbsp;java.lang.Shutdown&nbsp;from&nbsp;/home/ubuntu/jdk1.8.0_custom/jre/lib/rt.jar</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br /></span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">Loaded&nbsp;java.lang.Shutdown$Lock&nbsp;from&nbsp;/home/ubuntu/jdk1.8.0_custom/jre/lib/rt.jar</span><span style="color: #800000; font-weight: bold; ">]</span></div>从中可以看出，类java.util.HashMap确实是从rt.jar中加载到的。但理论上，可以通过自定义类加载器去打破委托模式，然而这就是另一个话题了。</div></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/428010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2015-11-01 20:06 <a href="http://www.blogjava.net/jiangshachina/archive/2015/11/01/428010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Play OpenJDK: 构建你自己的JDK(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2015/10/30/427994.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Fri, 30 Oct 2015 15:17:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2015/10/30/427994.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/427994.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2015/10/30/427994.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/427994.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/427994.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;"><span style="font-size: 14pt;">Play OpenJDK: 构建你自己的JDK</span></strong></div><br />计划使Play OpenJDK成为一组介绍如何使用并参与OpenJDK项目的系列文章。本文是该系列的第一篇文章，它基于OpenJDK 8的源代码介绍了构建一个属于自己的JDK方法。(2015.10.30最后更新)<br /><br /><strong><span style="font-size: 12pt;">1. OpenJDK</span></strong><br />曾经的Sun Microsystems. Inc，也就是Java语言的发明者，将它的JDK代码贡献出来，成立了一个开源项目，即<a href="http://openjdk.java.net/">OpenJDK</a>。<br />&nbsp;&nbsp;&nbsp; 同时，它也是一个社区。相关的公司，组织和个人在这个社区中协作开发OpenJDK。社区根据不同的领域或项目提供了一系列的<a href="http://mail.openjdk.java.net/mailman/listinfo">邮件列表</a>，利益相关方或对其感兴趣的个人都可以订阅这些邮件列表去进行关注和讨论。例如，Java核心API的邮件列表是core-libs-dev@openjdk.java.net，关于java.lang，java.util等核心API的新特性都会在这里进行讨论，并对其最终的实现代码进行审查。任何将要进入OpenJDK版本库的源代码，无论是产品代码(即，要随JDK发布的程序)，还是测试代码，都需要在社区中进行公开的代码审查。<br />&nbsp;&nbsp;&nbsp; 选择一个自己感兴趣的领域或项目，加入它的邮件列表，长期跟踪它的发展，看着专家们的讨论、争论推动JDK的演进，学习开发者们的API设计与代码实现，...，这些对自己的成长都是极有帮助的。也许，还能看到一些有趣的八卦;-)<br /><br /><strong><span style="font-size: 12pt;">2. 准备工作</span></strong><br />在几种主流操作系统，Linux(如Ubuntu和Fedora)，Windows(7和8)，MacOS(Lion和Moutain Lion)，中都可以构建OpenJDK，具体的细节可以参见<a href="https://java.net/projects/adoptopenjdk/pages/AdoptOpenJDKBuildInstructions">官方的一篇文档</a>。<br />&nbsp;&nbsp;&nbsp; 本文选择使用Ubuntu 14.04。坦白地说，选择使用Ubuntu，实是因为在Linux环境中构建OpenJDK非常简单。若在Windows中进行构建，则需要安装Visual Studio C++编译器。MacOS？嗯，我没有MBP。可能更多人平时是使用Windows，但安装一个Linux也不麻烦。先安装免费的VM工具<a href="https://www.virtualbox.org/">VirtaulBox</a>，再去下载Ubuntu的<a href="http://www.ubuntu.com/download/server">ISO文件</a>，然后使用VirtualBox去安装Ubuntu。VirtualBox简单易用，网上的相关资料也非常的多。<br />&nbsp;&nbsp;&nbsp; OpenJDK的源文件版本库基于<a href="https://www.mercurial-scm.org/">Mercurial</a>(hg)，它是一个与Git相似的分布式版本控制工具。在Ubuntu中安装Mercurial只需要执行命令<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">$ sudo&nbsp;apt</span><span style="color: #000000; ">-</span><span style="color: #000000; ">get&nbsp;install&nbsp;mercurial</span></div>&nbsp;&nbsp;&nbsp; OpenJDK中各项目的源代码版本库的路径均在http://hg.openjdk.java.net/之下，在这里可以找到6，7，8，9和Jigsaw的源代码。其中JDK 8的最新开发版本库路径为http://hg.openjdk.java.net/jdk8u/jdk8u-dev/。<br />&nbsp;&nbsp;&nbsp; 另外，构建OpenJDK时需要一个启动JDK，本文选择构建OpenJDK 8，那么启动JDK的版本应不低于7。<br /><br /><strong><span style="font-size: 12pt;">3. 下载源代码</span></strong><br />克隆版本库，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">$&nbsp;hg&nbsp;clone&nbsp;http</span><span style="color: #000000; ">://</span><span style="color: #000000; ">hg</span><span style="color: #000000; ">.</span><span style="color: #000000; ">openjdk</span><span style="color: #000000; ">.</span><span style="color: #000000; ">java</span><span style="color: #000000; ">.</span><span style="color: #000000; ">net</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk8u</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk8u</span><span style="color: #000000; ">-</span><span style="color: #000000; ">dev</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;jdk8</span><span style="color: #000000; ">-</span><span style="color: #000000; ">src<br />requesting&nbsp;all&nbsp;changes<br />adding&nbsp;changesets<br />adding&nbsp;manifests<br />adding&nbsp;file&nbsp;changes<br />added&nbsp;</span><span style="color: #800000; ">1570</span><span style="color: #000000; ">&nbsp;changesets&nbsp;with&nbsp;</span><span style="color: #800000; ">1958</span><span style="color: #000000; ">&nbsp;changes&nbsp;to&nbsp;</span><span style="color: #800000; ">141</span><span style="color: #000000; ">&nbsp;files<br />updating&nbsp;to&nbsp;branch&nbsp;default<br /></span><span style="color: #800000; ">85</span><span style="color: #000000; ">&nbsp;files&nbsp;updated</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800000; ">0</span><span style="color: #000000; ">&nbsp;files&nbsp;merged</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800000; ">0</span><span style="color: #000000; ">&nbsp;files&nbsp;removed</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800000; ">0</span><span style="color: #000000; ">&nbsp;files&nbsp;unresolved</span></div>进入源代码目录<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">$&nbsp;cd&nbsp;jdk8</span><span style="color: #000000; ">-</span><span style="color: #000000; ">src</span><span style="color: #000000; ">/</span></div>OpenJDK的源代码版本库实际上包含多个独立的子版本库，需要执行如下脚本去分别下载各个子版本库的源代码，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">$&nbsp;sh&nbsp;get_source</span><span style="color: #000000; ">.</span><span style="color: #000000; ">sh<br /></span><span style="color: #008000; ">#</span><span style="color: #008000; ">&nbsp;Repositories:&nbsp;&nbsp;corba&nbsp;jaxp&nbsp;jaxws&nbsp;langtools&nbsp;jdk&nbsp;hotspot&nbsp;nashorn</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;corba</span><span style="color: #000000; ">:</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;hg&nbsp;clone&nbsp;http</span><span style="color: #000000; ">://</span><span style="color: #000000; ">hg</span><span style="color: #000000; ">.</span><span style="color: #000000; ">openjdk</span><span style="color: #000000; ">.</span><span style="color: #000000; ">java</span><span style="color: #000000; ">.</span><span style="color: #000000; ">net</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk8u</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk8u</span><span style="color: #000000; ">-</span><span style="color: #000000; ">dev</span><span style="color: #000000; ">/</span><span style="color: #000000; ">corba&nbsp;corba<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jaxp</span><span style="color: #000000; ">:</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;hg&nbsp;clone&nbsp;http</span><span style="color: #000000; ">://</span><span style="color: #000000; ">hg</span><span style="color: #000000; ">.</span><span style="color: #000000; ">openjdk</span><span style="color: #000000; ">.</span><span style="color: #000000; ">java</span><span style="color: #000000; ">.</span><span style="color: #000000; ">net</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk8u</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk8u</span><span style="color: #000000; ">-</span><span style="color: #000000; ">dev</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jaxp&nbsp;jaxp<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;corba</span><span style="color: #000000; ">:</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;requesting&nbsp;all&nbsp;changes<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jaxp</span><span style="color: #000000; ">:</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;requesting&nbsp;all&nbsp;changes<br /></span><span style="color: #000000; "><img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span></div><br /><strong><span style="font-size: 12pt;">4. 构建</span></strong><br />调用configure进行预构建，其中的参数--with-boot-jdk用于指定启动JDK的路径。如果启动JDK的java命令已存在于PATH环境变量中，该参数可以忽略。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">$ sh&nbsp;configure&nbsp;</span><span style="color: #000000; ">--</span><span style="color: #000000; ">with</span><span style="color: #000000; ">-</span><span style="color: #000000; ">boot</span><span style="color: #000000; ">-</span><span style="color: #000000; ">jdk</span><span style="color: #000000; ">=<em>/</em></span><em><span style="color: #000000; ">path</span><span style="color: #000000; ">/</span><span style="color: #000000; ">to</span><span style="color: #000000; ">/</span><span style="color: #000000; ">boot</span><span style="color: #000000; ">/</span><span style="color: #000000; ">jdk</span></em></div>该过程会检测构建环境是否符合要求，如有问题，它会给出提示。比如，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">configure</span><span style="color: #000000; ">:</span><span style="color: #000000; ">&nbsp;error</span><span style="color: #000000; ">:</span><span style="color: #000000; ">&nbsp;Could&nbsp;not&nbsp;find&nbsp;X11&nbsp;libraries</span><span style="color: #000000; ">.</span><span style="color: #000000; ">&nbsp;You&nbsp;might&nbsp;be&nbsp;able&nbsp;to&nbsp;fix&nbsp;this&nbsp;by&nbsp;running&nbsp;</span><span style="color: #000000; font-weight: bold; ">'</span><span style="color: #000000; font-weight: bold; ">sudo&nbsp;apt-get&nbsp;install&nbsp;libX11-dev&nbsp;libxext-dev&nbsp;libxrender-dev&nbsp;libxtst-dev&nbsp;libxt-dev</span><span style="color: #000000; font-weight: bold; ">'</span><span style="color: #000000; ">.</span></div>此时根据提示安装所需要的库即可。再重新执行上述configure命令，可能还会提示缺少其它的库，那么再次根据提示进行安装。如此反复，只到预构建成功完成。<br />最后就是进行构建，直接执行如下命令，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">$ make&nbsp;all</span></div>在此过程中可以会遇到一些警告，不必理会，耐心等待...完成后，会出现如下的汇总信息，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "></span><div>----- Build times -------<br />Start 2015-10-30 22:11:10<br />End&nbsp;&nbsp; 2015-10-30 22:52:54<br />00:01:01 corba<br />00:01:08 demos<br />00:06:49 docs<br />00:19:37 hotspot<br />00:01:47 images<br />00:00:35 jaxp<br />00:00:49 jaxws<br />00:08:23 jdk<br />00:01:09 langtools<br />00:00:25 nashorn<br />00:41:44 TOTAL<br />-------------------------<br />Finished building OpenJDK for target 'all'<br /><span style="color: #000000;"></span></div></div>在当前路径下会生成一个build目录，构建好的JDK就在那里面。新JDK的具体路径类似于build/linux-x86_64-normal-server-release/images/jdk。可以执行如下命令去测试这个JDK，<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "></span><div>$ build/linux-x86_64-normal-server-release/images/jdk/bin/java -version<br />openjdk version "1.8.0-internal"<br />OpenJDK Runtime Environment (build 1.8.0-internal-ubuntu_2015_10_30_22_07-b00)<br />OpenJDK 64-Bit Server VM (build 25.66-b00, mixed mode)<br /><span style="color: #000000; "></span></div></div></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/427994.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2015-10-30 23:17 <a href="http://www.blogjava.net/jiangshachina/archive/2015/10/30/427994.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java并发基础实践--死锁(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/12/29/408180.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 29 Dec 2013 12:19:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/12/29/408180.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/408180.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/12/29/408180.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/408180.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/408180.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Java并发基础实践--死锁</span></strong></div>本文是<a href="http://www.blogjava.net/jiangshachina/category/53896.html">Java并发基础实践</a>系列中的一篇，介绍了最简单的死锁场景，并使用jstack产生的thread dump来查找死锁。(2013.12.29最后更新)<br /><br /><strong style="font-size: 12pt;">1. 死锁</strong><br style="font-size: 12pt;" />为了能够维护线程的安全性，Java提供的锁机制，但不恰当地使用锁则可能产生死锁。死锁是并发编程中一个无法绕开的问题。只要在一个任务中使用了一个以上的锁，那么就存在死锁的风险。<br />死锁产生的直接原因非常简单，即两个线程在相互等待对方所执有的锁。<br /><br /><strong><span style="font-size: 12pt;">2. 锁顺序死锁</span></strong><br />在死锁场景中，最典型的就是锁顺序死锁，代码清单1就是一个很常见的示例。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单1</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;DeadLock&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;Object&nbsp;leftLock&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Object();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;Object&nbsp;rightLock&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Object();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;leftRight()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;(leftLock)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;(rightLock)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">leftRight</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;rightLeft()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;(rightLock)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; ">&nbsp;(leftLock)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">leftRight</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DeadLock&nbsp;deadLock&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;DeadLock();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;t1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Runnable()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deadLock.leftRight();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;t2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Runnable()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deadLock.rightLeft();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t1.start();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t2.start();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br /><strong><span style="font-size: 12pt;">3. Thread Dump</span></strong><br />JDK提供了一组命令行工具，其中就包括jstack。通过jstack可以获取当前正运行的Java进程的java stack和native stack信息。如果Java进程崩溃了，也可以通过它来获取core file中的java stack和native stack信息，以方便我们定位问题。<br />为了能够使用jstack去输出目标Java进程的thread dump，首先必须要弄清楚在执行清单1的程序时，该程序的进程号。JDK提供的另一个命令行工具jps可以获取系统中所有Java进程的相关信息。<br />在命令行窗口中执行命令<em>jps</em>，即可以得到清单2所示的结果<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单2</em><br />C:\Documents&nbsp;and&nbsp;Settings\Administrator&gt;jps<br /></span><span style="color: #000000; ">2848</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">4552</span><span style="color: #000000; ">&nbsp;DeadLock<br /></span><span style="color: #000000; ">5256</span><span style="color: #000000; ">&nbsp;Jps</span></div>其中<em>4552</em>就是在笔者机器上执行程序DeadLock时所生成Java进程的进程号。<br />然后再执行命令<em>jstack 4552</em>，在笔者的机器上就会得到清单3所示的结果<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单3</em><br />C:\Documents&nbsp;and&nbsp;Settings\Administrator&gt;jstack&nbsp;</span><span style="color: #000000; ">4552</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">2013</span><span style="color: #000000; ">-</span><span style="color: #000000; ">12</span><span style="color: #000000; ">-</span><span style="color: #000000; ">29</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">18</span><span style="color: #000000; ">:</span><span style="color: #000000; ">45</span><span style="color: #000000; ">:</span><span style="color: #000000; ">41</span><span style="color: #000000; "><br />Full&nbsp;thread&nbsp;dump&nbsp;Java&nbsp;HotSpot(TM)&nbsp;Client&nbsp;VM&nbsp;(</span><span style="color: #000000; ">23.25</span><span style="color: #000000; ">-b01&nbsp;mixed&nbsp;mode</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;sharing):<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">DestroyJavaVM</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">6</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x00878800&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0xd00&nbsp;waiting&nbsp;on&nbsp;condition&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x00000000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;RUNNABLE<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">6</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b56c00&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x14ec&nbsp;waiting&nbsp;for&nbsp;monitor&nbsp;entry&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x02fdf000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;BLOCKED&nbsp;(on&nbsp;object&nbsp;monitor)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock.rightLeft(DeadLock.java:</span><span style="color: #000000; ">33</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;&lt;0x22be6598&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22be65a0&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock$</span><span style="color: #000000; ">2</span><span style="color: #000000; ">.run(DeadLock.java:</span><span style="color: #000000; ">53</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.run(Thread.java:</span><span style="color: #000000; ">724</span><span style="color: #000000; ">)<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">6</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b55c00&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x354&nbsp;waiting&nbsp;for&nbsp;monitor&nbsp;entry&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x02f8f000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;BLOCKED&nbsp;(on&nbsp;object&nbsp;monitor)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock.leftRight(DeadLock.java:</span><span style="color: #000000; ">19</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;&lt;0x22be65a0&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22be6598&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(DeadLock.java:</span><span style="color: #000000; ">45</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.run(Thread.java:</span><span style="color: #000000; ">724</span><span style="color: #000000; ">)<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Service&nbsp;Thread</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;daemon&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">6</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b34800&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x133c&nbsp;runnable&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x00000000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;RUNNABLE<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">C1&nbsp;CompilerThread0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;daemon&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b13800&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x10fc&nbsp;waiting&nbsp;on&nbsp;condition&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x00000000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;RUNNABLE<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Attach&nbsp;Listener</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;daemon&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b11c00&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x1424&nbsp;waiting&nbsp;on&nbsp;condition&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x00000000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;RUNNABLE<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Signal&nbsp;Dispatcher</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;daemon&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b10800&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x1100&nbsp;runnable&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x00000000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;RUNNABLE<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Finalizer</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;daemon&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">8</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02af4c00&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x1238&nbsp;in&nbsp;Object.wait()&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x02daf000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;WAITING&nbsp;(on&nbsp;object&nbsp;monitor)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Object.wait(Native&nbsp;Method)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;on&nbsp;&lt;0x22b60fb8&gt;&nbsp;(a&nbsp;java.lang.ref.ReferenceQueue$Lock)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:</span><span style="color: #000000; ">135</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22b60fb8&gt;&nbsp;(a&nbsp;java.lang.ref.ReferenceQueue$Lock)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:</span><span style="color: #000000; ">151</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:</span><span style="color: #000000; ">189</span><span style="color: #000000; ">)<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Reference&nbsp;Handler</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;daemon&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02af0000&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x12e8&nbsp;in&nbsp;Object.wait()&nbsp;</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0x02d5f000</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;WAITING&nbsp;(on&nbsp;object&nbsp;monitor)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Object.wait(Native&nbsp;Method)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;on&nbsp;&lt;0x22b60da0&gt;&nbsp;(a&nbsp;java.lang.ref.Reference$Lock)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Object.wait(Object.java:</span><span style="color: #000000; ">503</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.ref.Reference$ReferenceHandler.run(Reference.java:</span><span style="color: #000000; ">133</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22b60da0&gt;&nbsp;(a&nbsp;java.lang.ref.Reference$Lock)<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">VM&nbsp;Thread</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02aee400&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x129c&nbsp;runnable<br /><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">VM&nbsp;Periodic&nbsp;Task&nbsp;Thread</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;prio</span><span style="color: #000000; ">=</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&nbsp;tid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x02b48000&nbsp;nid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0x89c&nbsp;waiting&nbsp;on&nbsp;condition<br /><br />JNI&nbsp;global&nbsp;references:&nbsp;</span><span style="color: #000000; ">117</span><span style="color: #000000; "><br /><br /><br />Found&nbsp;one&nbsp;Java-level&nbsp;deadlock:<br /></span><span style="color: #000000; ">=============================</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;monitor&nbsp;0x02af4a3c&nbsp;(object&nbsp;0x22be6598</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;a&nbsp;java.lang.Object)</span><span style="color: #000000; ">,</span><span style="color: #000000; "><br />&nbsp;&nbsp;which&nbsp;is&nbsp;held&nbsp;by&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;monitor&nbsp;0x02af310c&nbsp;(object&nbsp;0x22be65a0</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;a&nbsp;java.lang.Object)</span><span style="color: #000000; ">,</span><span style="color: #000000; "><br />&nbsp;&nbsp;which&nbsp;is&nbsp;held&nbsp;by&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span><span style="color: #000000; "><br /><br />Java&nbsp;stack&nbsp;information&nbsp;for&nbsp;the&nbsp;threads&nbsp;listed&nbsp;above:<br /></span><span style="color: #000000; ">===================================================</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock.rightLeft(DeadLock.java:</span><span style="color: #000000; ">33</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;&lt;0x22be6598&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22be65a0&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock$</span><span style="color: #000000; ">2</span><span style="color: #000000; ">.run(DeadLock.java:</span><span style="color: #000000; ">53</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.run(Thread.java:</span><span style="color: #000000; ">724</span><span style="color: #000000; ">)<br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock.leftRight(DeadLock.java:</span><span style="color: #000000; ">19</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;&lt;0x22be65a0&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22be6598&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(DeadLock.java:</span><span style="color: #000000; ">45</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.run(Thread.java:</span><span style="color: #000000; ">724</span><span style="color: #000000; ">)<br /><br />Found&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;deadlock.</span></div>在上述输出中，我们可以很明确地看到一个死锁<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;monitor&nbsp;0x02af4a3c&nbsp;(object&nbsp;0x22be6598</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;a&nbsp;java.lang.Object)</span><span style="color: #000000; ">,</span><span style="color: #000000; "><br />&nbsp;&nbsp;which&nbsp;is&nbsp;held&nbsp;by&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;monitor&nbsp;0x02af310c&nbsp;(object&nbsp;0x22be65a0</span><span style="color: #000000; ">,</span><span style="color: #000000; ">&nbsp;a&nbsp;java.lang.Object)</span><span style="color: #000000; ">,</span><span style="color: #000000; "><br />&nbsp;&nbsp;which&nbsp;is&nbsp;held&nbsp;by&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span></div>并且它还标明了程序是在哪个地方时发现了上述死锁<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-1</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock.rightLeft(DeadLock.java:</span><span style="color: #000000; ">33</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;&lt;0x22be6598&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22be65a0&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock$</span><span style="color: #000000; ">2</span><span style="color: #000000; ">.run(DeadLock.java:</span><span style="color: #000000; ">53</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.run(Thread.java:</span><span style="color: #000000; ">724</span><span style="color: #000000; ">)<br /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">Thread-0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock.leftRight(DeadLock.java:</span><span style="color: #000000; ">19</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;waiting&nbsp;to&nbsp;lock&nbsp;&lt;0x22be65a0&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;locked&nbsp;&lt;0x22be6598&gt;&nbsp;(a&nbsp;java.lang.Object)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;concurrency.deadlock.DeadLock$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.run(DeadLock.java:</span><span style="color: #000000; ">45</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.run(Thread.java:</span><span style="color: #000000; ">724</span><span style="color: #000000;">)</span></div><br /><strong><span style="font-size: 12pt;">4. 小结</span></strong><br />死锁产生的直接原因非常简单，即两个线程在相互等待对方所执有的锁。锁顺序死锁是其中最经典的场景，此外还有动态的锁顺序死锁。虽然表现形式有所不同，但本质上都是两个线程在以不同的顺序来获取相同锁时，发生了死锁问题。<br />使用thread dump可以帮助我们分析死锁产生的原因。除了直接使用jstack命令来获取thread dump输出以外，JDK还提供了jvisualvm工具，它能以可视化的方式展示Java程序的进程号并导出thread dump。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/408180.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2013-12-29 20:19 <a href="http://www.blogjava.net/jiangshachina/archive/2013/12/29/408180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java并发基础实践--分而治之(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/10/23/405577.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Wed, 23 Oct 2013 15:27:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/10/23/405577.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/405577.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/10/23/405577.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/405577.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/405577.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java并发基础实践--分而治之本系列的第三篇文章将以实现一个极简单的查找最大数的任务为例，分别给出了四个版本：1.顺序执行；2.基于传统的Thread.join()；3.基于并发工具包的Future；4.基于JDK 7引入的Fork/Join框架。(2013.10.25最后更新)&nbsp;&nbsp;&nbsp; 分而治之(Divide-and-Conquer)是解决复杂问题的常用方法。在并发...&nbsp;&nbsp;<a href='http://www.blogjava.net/jiangshachina/archive/2013/10/23/405577.html'>阅读全文</a><img src ="http://www.blogjava.net/jiangshachina/aggbug/405577.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2013-10-23 23:27 <a href="http://www.blogjava.net/jiangshachina/archive/2013/10/23/405577.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java并发基础实践--退出任务II(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/10/07/404690.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 07 Oct 2013 08:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/10/07/404690.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/404690.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/10/07/404690.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/404690.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/404690.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Java并发基础实践--退出任务II</span></strong></div>在<a href="http://www.blogjava.net/jiangshachina/category/53896.html">本系列</a>的<a href="http://www.blogjava.net/jiangshachina/archive/2013/09/21/404269.html">上一篇</a>中所述的退出并发任务的方式都是基于JDK 5之前的API，本文将介绍使用由JDK 5引入的并发工具包中的API来退出任务。(2013.10.08最后更新)<br /><br />&nbsp;&nbsp;&nbsp; 在本系列的前一篇中讲述了三种退出并发任务的方式--停止线程；可取消的任务；中断，但都是基于JDK 5之前的API。本篇将介绍由JDK 5引入的java.concurrent包中的Future来取消任务的执行。<br /><br /><strong style="font-size: 12pt;">1. Future模式</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; Future是并发编程中的一种常见设计模式，它相当于是Proxy模式与Thread-Per-Message模式的结合。即，每次都创建一个单独的线程去执行一个耗时的任务，并且创建一个Future对象去持有实际的任务对象，在将来需要的时候再去获取实际任务的执行结果。<br />依然先创建一个用于扫描文件的任务FileScannerTask，如代码清单1所示，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单1</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScannerTask&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;File&nbsp;root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;FileScannerTask(File&nbsp;root)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(root&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">root.exists()&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">root.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;IllegalArgumentException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">root&nbsp;must&nbsp;be&nbsp;directory</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;root;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(root);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;filePath&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.getAbsolutePath();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filePaths.add(filePath);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(parent.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File[]&nbsp;children&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.listFiles();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(children&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(File&nbsp;child&nbsp;:&nbsp;children)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(child);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;getFilePaths()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">)&nbsp;filePaths.clone();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>此处的文件扫描任务，提供了一个getFilePaths()方法以允许随时都可以取出当前已扫描过的文件的路径(相当于一个任务快照)。然后，创建一个针对该任务的Future类，如代码清单2所示，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单2</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScannerFuture&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;FileScannerTask&nbsp;task&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;FileScannerFuture(FileScannerTask&nbsp;task)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(task).start();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.task&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;task;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;getResult()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;task.getFilePaths();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>FileScannerFuture持有FileScannerTask的引用，并创建一个独立的线程来执行该任务。在任务的执行过程中，应用程序可以在"未来"的某个时刻去获取一个任务的快照，如代码清单3所示，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单3</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;FileScannerFuture&nbsp;future&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScannerFuture(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScannerTask(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;future.getResult();<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(filePaths1.size());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;future.getResult();<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(filePaths2.size());<br />}</span></div><br /><strong style="font-size: 12pt;">2. 使用并发工具包中的Future实现</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; 前面所展示的Future实现十分的简陋，没有实际应用的意义。使用FileScannerFuture，应用程序在获取filePaths时，无法得知其获取的是否为最终结果，即无法判断FileScannerTask是否已经完成。而且，也不能在必要时停止FileScannerTask的执行。毫无疑问，由JDK 5引入的并发工具包肯定会提供此类实用工具，如FutureTask。为了使用并发工具包中的Future，需要修改前述的FileScannerTask实现，让其实现Callable接口，如代码清单4所示，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单4</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScannerTask&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Callable</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;File&nbsp;root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;FileScannerTask(File&nbsp;root)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(root&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">root.exists()&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">root.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;IllegalArgumentException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">root&nbsp;must&nbsp;be&nbsp;directory</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;root;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;call()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(root);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;filePaths;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;filePath&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.getAbsolutePath();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filePaths.add(filePath);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(parent.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File[]&nbsp;children&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.listFiles();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(children&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(File&nbsp;child&nbsp;:&nbsp;children)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(child);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;getFilePaths()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">)&nbsp;filePaths.clone();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>应用程序也要相应的修改成如代码清单5所示，使用ExecutorService来提交任务，并创建一个Future/FutureTask实例。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单5</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;ExecutorService&nbsp;executorService&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Executors.newCachedThreadPool();<br />&nbsp;&nbsp;&nbsp;&nbsp;Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;future&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;executorService.submit(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScannerTask(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;future.get();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(filePaths.size());<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(ExecutionException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;executorService.shutdown();<br />}</span></div>此处就是调用Future.get()方法来获取任务的执行结果，如果任务没有执行完毕，那么该方法将会被阻塞。该Future实现的好处就是，正常情况下，只有在任务执行完毕之后才能获取其结果，以保证该结果是最终执行结果。<br /><br /><strong><span style="font-size: 12pt;">3. 使用Future取消任务</span></strong><br />&nbsp;&nbsp;&nbsp; Future除了定义有可获取执行结果的get方法(get()以及get(long timeout, TimeUnit unit))，还定义了三个方法：cancel()，isCancelled()以及isDone()，用于取消任务，以及判定任务是否已被取消、已执行完毕。如代码清单6所示，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单6</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">V</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;cancel(</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;mayInterruptIfRunning);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;isCancelled();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;isDone();<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div>其中，cancel()方法中的boolean参数若为true，表示在取消该任务时，若执行该任务的线程仍在运行中，则对其进行中断。如代码清单7所示，若任务执行超时了，那么就取消它。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单7</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;ExecutorService&nbsp;executorService&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Executors.newCachedThreadPool();<br />&nbsp;&nbsp;&nbsp;&nbsp;Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;future&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;executorService.submit(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScannerTask(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;future.get(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;TimeUnit.SECONDS);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(filePaths.size());<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(ExecutionException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(TimeoutException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">finally</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;future.cancel(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;executorService.shutdown();<br />}</span></div>在实际应用中，取消任务的原由肯定不仅仅只是超时这么简单，还可能是由于接受到了用户的指令。此时，则可能会从另一个独立线程去取消该任务。除了取消任务之外，有时还需要取出任务中已经生成的部分结果。但为了能够响应任务的退出，首先需要修改FileScannerTask，使得当任务被取消(中断)时，任务能够真正的快速停止并返回，如代码清单8所示，<br /><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单8</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScannerTask&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Callable</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(Thread.currentThread().isInterrupted())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;filePath&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.getAbsolutePath();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filePaths.add(filePath);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(parent.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File[]&nbsp;children&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.listFiles();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(children&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(File&nbsp;child&nbsp;:&nbsp;children)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(child);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div>相应地修改应用程序的代码，如代码清单9所示，</div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单9</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;ExecutorService&nbsp;executorService&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Executors.newCachedThreadPool();<br />&nbsp;&nbsp;&nbsp;&nbsp;FileScannerTask&nbsp;task&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScannerTask(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;future&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;executorService.submit(task);<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Runnable()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;future.cancel(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}).start();<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;future.get();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(filePaths.size());<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(ExecutionException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(CancellationException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;task.getFilePaths();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Partly&nbsp;result:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;filePaths.size());<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;executorService.shutdown();<br />}</span></div>由上可知，此处使用Future.cancel(true)的本质依然是利用了线程的中断机制。<br /><br /><strong style="font-size: 12pt;">4. 小结</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; 使用Future可以在任务启动之后的特定时机再去获取任务的执行结果。由JDK 5引入的并发工具包中提供的Future实现不仅可以获取任务的执行结果，还可以用于取消任务的执行。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/404690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2013-10-07 16:55 <a href="http://www.blogjava.net/jiangshachina/archive/2013/10/07/404690.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java并发基础实践--退出任务I(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/09/21/404269.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 21 Sep 2013 11:11:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/09/21/404269.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/404269.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/09/21/404269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/404269.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/404269.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java并发基础实践--退出任务I</span></strong></div><span style="font-size: 10pt;">计划写一个"<a href="http://www.blogjava.net/jiangshachina/category/53896.html">Java并发基础实践</a>"系列，算作本人对Java并发学习与实践的简单总结。本文是该系列的第一篇，介绍了退出并发任务的最简单方法。(2013.09.25最后更新)</span><br /><br /><span style="font-size: 10pt;">在一个并发任务被启动之后，不要期望它总是会执行完成。由于时间限制，资源限制，用户操作，甚至是任务中的异常(尤其是运行时异常)，...都可能造成任务不能执行完成。如何恰当地退出任务是一个很常见的问题，而且实现方法也不一而足。</span><br /><br /><strong><span style="font-size: 12pt;">1. 任务</span></strong><br /><span style="font-size: 10pt;">创建一个并发任务，递归地获取指定目录下的所有子目录与文件的绝对路径，最后再将这些路径信息保存到一个文件中，如代码清单1所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单1</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScanner&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;File&nbsp;root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;FileScanner1(File&nbsp;root)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(root&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">root.exists()&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">root.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;IllegalArgumentException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">root&nbsp;must&nbsp;be&nbsp;legal&nbsp;directory</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;root;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(root);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saveFilePaths();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Exception&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;filePath&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.getAbsolutePath();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filePaths.add(filePath);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(parent.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File[]&nbsp;children&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.listFiles();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(File&nbsp;child&nbsp;:&nbsp;children)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(child);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;saveFilePaths()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileWriter&nbsp;fos&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileWriter(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(root.getAbsoluteFile()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;File.separator&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">filePaths.out</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(String&nbsp;filePath&nbsp;:&nbsp;filePaths)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fos.write(filePath&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">\n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fos.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br /><strong><span style="font-size: 12pt;">2. 停止线程</span></strong><br /><span style="font-size: 10pt;">有一个很直接，也很干脆的方式来停止线程，就是调用Thread.stop()方法，如代码清单2所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单2</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;FileScanner&nbsp;task&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScanner(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;taskThread&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(task);<br />&nbsp;&nbsp;&nbsp;&nbsp;taskThread.start();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;taskThread.stop();<br />}</span></div><span style="font-size: 10pt;">但是，地球人都知道Thread.stop()在很久很久之前就不推荐使用了。根据<a href="http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">官方文档</a>的介绍，该方法存在着固有的不安全性。当停止线程时，将会释放该线程所占有的全部监视锁，这就会造成受这些锁保护的对象的不一致性。在执行清单2的应用程序时，它的运行结果是不确定的。它可能会输出一个文件，其中包含部分的被扫描过的目录和文件。但它也很有可能什么也不输出，因为在执行FileWriter.write()的过程中，可能由于线程停止而造成了I/O异常，使得最终无法得到输出文件。</span><br /><br /><strong><span style="font-size: 12pt;">3. 可取消的任务</span></strong><br /><span style="font-size: 10pt;">另外一种十分常见的途径是，在设计之初，我们就使任务是可被取消的。一般地，就是提供一个取消标志或设定一个取消条件，一旦任务遇到该标志或满足了取消条件，就会结束任务的执行。如代码清单3所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单3</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScanner&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;File&nbsp;root&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;filePaths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;cancel&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;FileScanner(File&nbsp;root)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(cancel)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;filePath&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.getAbsolutePath();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filePaths.add(filePath);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(parent.isDirectory())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File[]&nbsp;children&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;parent.listFiles();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(File&nbsp;child&nbsp;:&nbsp;children)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travleFiles(child);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;saveFilePaths()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;cancel()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cancel&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">新的FileScanner实现提供一个cancel标志，travleFiles()会遍历新的文件之前检测该标志，若该标志为true，则会立即返回。代码清单4是使用新任务的应用程序。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单4</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;FileScanner&nbsp;task&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScanner(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;taskThread&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(task);<br />&nbsp;&nbsp;&nbsp;&nbsp;taskThread.start();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;task.cancel();<br />}</span></div><span style="font-size: 10pt;">但有些时候使用可取消的任务，并不能快速地退出任务。因为任务在检测取消标志之前，可能正处于等待状态，甚至可能被阻塞着。对清单2中的FileScanner稍作修改，让每次访问新的文件之前先睡眠10秒钟，如代码清单5所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单5</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScanner&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(cancel)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;saveFilePaths()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;cancel()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cancel&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">再执行清单3中的应用程序时，可能发现任务并没有很快速的退出，而是又等待了大约7秒钟才退出。如果在检查cancel标志之前要先获取某个受锁保护的资源，那么该任务就会被阻塞，并且无法确定何时能够退出。对于这种情况，就需要使用中断了。</span><br /><br /><strong><span style="font-size: 12pt;">4. 中断</span></strong><br /><span style="font-size: 10pt;">中断是一种协作机制，它并不会真正地停止一个线程，而只是提醒线程需要被中断，并将线程的中断状态设置为true。如果线程正在执行一些可抛出InterruptedException的方法，如Thread.sleep()，Thread.join()和Object.wait()，那么当线程被中断时，上述方法就会抛出InterruptedException，并且中断状态会被重新设置为false。任务程序只要恰当处理该异常，就可以正常地退出任务。对清单5再稍作修改，即，如果任务在睡眠时遇上了InterruptedException，那么就取消任务。如代码清单6所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单6</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScanner&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cancel();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(cancel)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div><span style="font-size: 10pt;">同时将清单4中的应用程序，此时将调用Thread.interrupt()方法去中断线程，如代码清单7所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单7</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;FileScanner3&nbsp;task&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileScanner3(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">C:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;taskThread&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Thread(task);<br />&nbsp;&nbsp;&nbsp;&nbsp;taskThread.start();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;taskThread.interrupt();<br />}</span></div><span style="font-size: 10pt;">或者更进一步，仅使用中断状态来控制程序的退出，而不再使用可取消的任务(即，删除cancel标志)，将清单6中的FileScanner修改成如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单8</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScanner&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.currentThread().interrupt();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(Thread.currentThread().isInterrupted())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div><span style="font-size: 10pt;">再次执行清单7的应用程序后，新的FileScanner也能即时的退出了。值得注意的是，因为当sleep()方法抛出InterruptedException时，该线程的中断状态将又会被设置为false，所以必须要再次调用interrupt()方法来保存中断状态，这样在后面才可以利用中断状态来判定是否需要返回travleFiles()方法。当然，对于此处的例子，在收到InterruptedException时也可以选择直接返回，如代码清单9所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><em>清单9</em><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;FileScanner&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Runnable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;travleFiles(File&nbsp;parent)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div><br /><strong><span style="font-size: 12pt;">5 小结</span></strong><br /><span style="font-size: 10pt;">本文介绍了三种简单的退出并发任务的方法：停止线程；使用可取消任务；使用中断。毫无疑问，停止线程是不可取的。使用可取消的任务时，要避免任务由于被阻塞而无法及时，甚至永远无法被取消。一般地，恰当地使用中断是取消任务的首选方式。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/404269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2013-09-21 19:11 <a href="http://www.blogjava.net/jiangshachina/archive/2013/09/21/404269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle OpenWorld 2013(上海)(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/08/02/402274.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Fri, 02 Aug 2013 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/08/02/402274.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/402274.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/08/02/402274.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/402274.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/402274.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt; font-family: Courier;">Oracle OpenWorld 2013(上海)</span></strong></div><span style="font-size: 10pt; font-family: Courier;">Oracle OpenWorld 2013(上海)已经结束，我全程参与了此次技术大会。本文是我参会的所见、所闻、所思与所感，有文，有图，且无码。(2013.08.03最后更新)</span><br /><br /><span style="font-size: 10pt; font-family: Courier;">Oracle OpenWorld 2013(Shanghai) lasted four days, and included over 300 keynotes, sessions and hands-on labs. 18000+ attendees, including company leaders, industries experts and developers, joined the event, which was said the biggest IT activity in Asia-Pacific area as never before.</span><br /><br /><strong><span style="font-size: 12pt; font-family: Courier;">Sessions</span></strong><br /><strong style="font-size: 10pt; font-family: Courier;">* Java strategy/technical keynote</strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">Every JavaOne conference must has such keynote, which introduces and demonstrates the latest new JDK versions and features, and prospects upcoming JDK version and features. All parts of Java ecosystem, including JME, JSE, JEE and JavaFX, are involved in the keynote. I was deeply impressed by JavaFX demo by Jim Weaver. The Java champion played musical instruments with a JavaFX application; that's really cool. In this JavaOne, JavaFX has a separated track, and the technology is very highlighted by Oracle. The software giant expects JavaFX to enhance Java's performance on rich application. But outspokenly, it's difficult to take end users to install a JRE beforehand if they just want to run a desktop application.</span><br style="font-size: 10pt; font-family: Courier;" /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_05.jpg" height="375" width="500" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_06.jpg" height="375" width="500" /><br /><br /><strong style="font-size: 10pt; font-family: Courier;">* Why should I switch to Java SE 7?</strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">JDK 6 has rested in peace(exactly, the version isn't completely frozen), and JDK 7 has been released for two years, and even JDK 8 will come next March. But do you migrate your JRE to Java 7? Strings in switch statements, diamond operator, try with resources, multi-catch statement, ... Do you like the compiler sugar? Anyway, the features could improve development productivity. But fork-join framework really touches my heart. Because of complete backward compatibility, you just need to run your application in the new platform, and the performance could be accelerated.</span><br style="font-size: 10pt; font-family: Courier;" /><br style="font-size: 10pt; font-family: Courier;" /><strong style="font-size: 10pt; font-family: Courier;"><span style="font-size: 10pt;">* The road to Lambda</span></strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">Java has been being criticis</span><span style="font-size: 10pt; font-family: Courier;">ed for implementing modern language features, like closure. The argument around closure implementation has lasted for a long time. Finally, Lambda, the closure for Java, has been finished, and will be distributed with JDK 8. Lambda is regarded as the biggest change since Generics in Java 5. Function Programming (FP) is popular, and Java developers can utilise Lambda expression to apply FP. And JDK 8 extends Collection framework to take advantage of Lambda as powerful as possible. For example, currently we need an iterator or loop to iterate a Collection container, that's so-called external iteration. JDK 8 adds a new method forEach() for Collection interface, and we can iterate a Collection object via internal iteration, that means it's no need to care the iteration details by developers. In fact, it's impossible to debug the iterating process. And the process may apply fork-join if possible to improve performance. That sounds cool!</span><br style="font-size: 10pt; font-family: Courier;" /><br style="font-size: 10pt; font-family: Courier;" /><strong style="font-size: 10pt; font-family: Courier;">* Effective Scala</strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">Dynamic programming languages are emerging, and more languages, such as Groovy, Scala and Clojure, can run on JVM, and JDK 7 introduced a new instruction "invokeDynamic" to support dynamic languages better. Therefore, I have to pay some attention to other JVM languages. The session "Effective Scala" was provided by TypeSafe, a company founded by Scala creators; Spring founder Rod Johnson joined the company as member of board of directors last year. The session simulated the style of Effective Java, and also introduced some best practices and coding regulations. Frankly, I don't know Scala syntax, like traits, totally, so I don't understand the lecture exactly. But I still obtained something new, like Cake pattern. Do you know the design pattern? I never hear it before this session, but the pattern is well-known in Scala world. It's time to learn another language, and I think Groovy is a good start-up due to it is closer to Java.</span><br style="font-size: 10pt; font-family: Courier;" /><strong style="font-size: 10pt; font-family: Courier;"><br />* Taobao GCIH</strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">Taobao creates GCIH (GC Invisible Heap) based on Oracle VM Hotspot, and allows different VM to access a shared heap. In Taobao business scenario, the shared heap only stores read-only objects. All of the objects are initialized after the application is launched, and not be cleaned before the application shutdowns. Therefore, single VM can consume less memory, that means we can deploy more VMs in single machine. And GCIH cannot be accessed by GC so that reducing the garbage collection overhead.</span><br style="font-size: 10pt; font-family: Courier;" /><br style="font-size: 10pt; font-family: Courier;" /><strong style="font-family: Courier;"><span style="font-size: 10pt;">* What do you mean, backwards compatibility?</span></strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">10gen, the company behind MongoDB, presented the topic. Because old MongoDB driver isn't well designed, so it's difficult to understand and use the driver API. For example, the driver has a lot of find() methods, but how do I know which one is my want. According to the session, method chaining not only makes codes more readable, and the idiom benefits the backward compatibility as well. Generally, method chaining only handle one parameter at each invoking, and application codes don't care the sequence of invoking. In this case, it's no need to provide the methods with several arguments, and the methods are possible to be deprecated in later time due to their ambiguity. Of course, method chaining isn't designed for backward compatibility. And applying backward compatibility is still a hard job, because you don't know what would happen in the future. In fact, many and many APIs are deprecated in later JDK versions.</span><br style="font-size: 10pt; font-family: Courier;" /><br style="font-size: 10pt; font-family: Courier;" /><strong style="font-family: Courier;"><span style="font-size: 12pt;">Appreciation Party</span></strong><br style="font-size: 10pt; font-family: Courier;" /><span style="font-size: 10pt; font-family: Courier;">In the third evening, Oracle catered a small but exciting party to every attendee. Beer, snacks, professional band, beautiful girls, hot performances, ... were on the stage. All of performances were shown by Oracle employees, and we just rock!</span><br style="font-size: 10pt; font-family: Courier;" /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_07.jpg" height="375" width="500" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_09.jpg" height="375" width="500" /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_11.jpg" height="533" width="400" /><br /><br /><strong><span style="font-size: 12pt; font-family: Courier;">I was on the scene</span></strong><br /><span style="font-family: Courier;">In OTN lounge, I communicated with Jim Weaver and his assistant about JavaFX and UI testing. In this event, my small dream, taking a photo with Java mascot -- Duke, came true </span><span style="font-family: Courier;">^_^ Additionally, don't forget iron man :-)</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/js_01.jpg" height="533" width="400" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/js_02.jpg" height="533" width="400" /><br /><br /><strong style="font-size: 12pt; font-family: Courier;">Event Organization</strong><br style="font-size: 12pt; font-family: Courier;" /><span style="font-family: Courier;">This is my sixth SunTechDays/OOW, and I must say that the conference was organized the best this time. Professional agenda, considerate attendee service, delicious dessert and drinking, all of them, except for lunch, must be delighted.</span><br style="font-family: Courier;" /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_01.jpg" height="375" width="500" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_02.jpg" height="375" width="500" /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_04.jpg" height="375" width="500" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_10.jpg" height="375" width="500" /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_13.jpg" height="533" width="400" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/oow2013/oow_12.jpg" height="533" width="400" /><br /><strong style="font-family: Courier;">Finally, I must say the event is well worth participating; join it, and enjoy it :-)</strong><strong><br /></strong></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/402274.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2013-08-02 11:50 <a href="http://www.blogjava.net/jiangshachina/archive/2013/08/02/402274.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>世界一直在变(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/07/14/401546.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 14 Jul 2013 03:15:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/07/14/401546.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/401546.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/07/14/401546.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/401546.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/401546.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt; font-family: Courier;">世界一直在变</span></strong></div><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; 最近有点儿闲，看了一些文章，有了一些感触，写成一篇小文。软件世界真的变化很大，没有永恒的终结者。今天的终结者，明天可能就会被别人终结。道理大家都懂，但现实依然很残酷。(2013.07.29最后更新)</span><br /><br /><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; This week, I read some articles about some API and tools that developers, especially Java guys, must know. Fortunately, I really know some of them, but unfortunately, I really miss something.</span><br /><br /><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; Please let me introduce some cases at first:</span><br /><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; 1. In our real projects, we only use JDK 6, but the version had been in End-Of-Life; we never touch JDK 7, but JDK 8 is upcoming. I don't know how much time we would spend on accepting <a href="http://openjdk.java.net/projects/lambda/"><span style="font-family: Courier;">Lambda</span></a></span><span style="font-size: 10pt; font-family: Courier;"> expression. In fact, at present, a lot of Java developers cannot understand Generics exactly, however the syntax has been introduced for more than 8 years. Of course, Java Generics is a bit ambiguous, so it may be difficult to understand.</span><br /><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; 2. Ant was ever the standard for building, and it still being used by many projects, even new ones. Maven was designed to terminate Ant due to the older cannot make life easy. Some conceptions of Maven, such as build life cycle, dependency management, default directory structure, are very advanced. But Maven dependency and transitive dependency management is nightmare, you have to include/exclude this or that. And extending Maven is also a hard job. I have real experience on both of them, I even wrote some popular preliminary <a href="http://www.blogjava.net/jiangshachina/category/16092.html">blogs</a> about Maven several years ago. But what I really don't know? I don't know Maven is becoming legacy, and worse, a new super star <a href="http://www.gradle.org/"><span style="font-family: Courier;">Gradle</span></a></span><span style="font-size: 10pt; font-family: Courier;"> is on stage. Outspokenly, I never hear of the artifact before this week :-( Outstanding Spring framework is a very case about the trend. At beginning, obviously Spring is built by Ant, then the framework switched to Maven some years ago, but last year Spring migrated to Gradle.</span><br /><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; 3. Google-Collections was well-known if you used it or not, and I know <a href="http://code.google.com/p/guava-libraries/"><span style="font-family: Courier;">Guava</span></a></span><span style="font-size: 10pt; font-family: Courier;"> however I never use the API. But what I really don't know? I don't know google-collections was closed several years ago, and even it was combined by Guava, which is a new rock star in Java ecosystem.</span><br /><br /><span style="font-size: 10pt; font-family: Courier;">　　OH, something is born, and then grows, and then rests in peace. That's nature, and we have to face it, but why I don't know? Exactly, I have no idea.</span><br /><span style="font-size: 10pt; font-family: Courier;">　　World has been changing, and is changing faster as never before. How to keep us up-to-date with new fashion? I think the question may be asked by every "old" developer. After a long term career life, some of us may become veteran, but absolutely, it's impossible that everyone become expert, particularly the expert in underlying fields. We just be proficient in some programming languages, frameworks, APIs, or tools. So we must update our brains continuously.</span><br /><span style="font-size: 10pt; font-family: Courier;">　　Maybe the issue is one of the middle life crisis problems, good luck for you and me :-)</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/401546.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2013-07-14 11:15 <a href="http://www.blogjava.net/jiangshachina/archive/2013/07/14/401546.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解Java对象序列化(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 13 Feb 2012 15:22:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/369898.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html#Feedback</comments><slash:comments>20</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/369898.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/369898.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 理解Java对象序列化关于Java序列化的文章早已是汗牛充栋了，本文是对我个人过往学习，理解及应用Java序列化的一个总结。此文内容涉及Java序列化的基本原理，以及多种方法对序列化形式进行定制。在撰写本文时，既参考了Thinking in Java, Effective Java，JavaWorld，developerWorks中的相关文章和其它网络资料，也加入了自己的实践经验与理解，文、码并茂...&nbsp;&nbsp;<a href='http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html'>阅读全文</a><img src ="http://www.blogjava.net/jiangshachina/aggbug/369898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2012-02-13 23:22 <a href="http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索JUnit4扩展：深入Rule(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 03 Jan 2012 16:13:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/367802.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/367802.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/367802.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong style="font-size: 14pt;"><span style="font-size: 12pt;">探索JUnit4扩展：深入Rule</span><br /></strong></div><span style="font-size: 10pt;">本文是"探索JUnit4扩展"系列中的第三篇，将进一步探究Rule的应用，展示如何使用Rule来替代@BeforeClass，@AfterClass，@Before和@After的功能。(2012.01.04最后更新)</span><br /><br /><span style="font-size: 10pt;">在本系列的第二篇<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html">《探索JUnit4扩展：应用Rule》</a>中提到，可以使用Rule替代现有的大部分Runner扩展，而且也不提倡对Runner中的withBefores()，withAfters()等方法进行扩展。本文将介绍如何使用Rule去实现@Before，@After和@BeforeClass的相同功能。</span><br /><br /><strong><span style="font-size: 12pt;">1. BaseRule</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 首先要创建一个较通用的TestRule实现BaseRule，它会释放出两个扩展点，一个在执行测试方法之前，before()；另一个在执行测试方法之后after()。下面是该类的代码，</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">abstract</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;BaseRule&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;TestRule&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Statement&nbsp;apply(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RuleStatement(base,&nbsp;description);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;RuleStatement&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;Statement&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;Statement&nbsp;base&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;Description&nbsp;description&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;RuleStatement(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.base&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;base;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.description&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;description;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;evaluate()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;before(base,&nbsp;description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base.evaluate();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">finally</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;after(base,&nbsp;description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;before(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;after(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">如果对JUnit4的源代码略有认知，可能会发现BaseRule与JUnit4提供的TestRule实现ExternalResource代码相似。关键的不同之处是，BaseRule中的before()与after()方法都提供了Statement与Description类型的参数，这使得它能够完成更复杂的工作。</span><br /><br /><strong><span style="font-size: 12pt;">2. CalculatorTest</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文使用的CalculatorTest将不使用@BeforeClass，@Before和@After，而会创建两个BaseRule的实例：一个用于替代@BeforeClass和@AfterClass(本系列目前还未使用过@AfterClass)，另一个则替代@Before和@After。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CalculatorTest&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DateFormat&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy-MM-dd_HH:mm:ss_SSS</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@ClassRule<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;BaseRule&nbsp;classRule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BaseRule()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;before(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Calculator();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Rule<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;BaseRule&nbsp;rule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BaseRule()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;before(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBeforeLog(description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;after(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printAfterLog(description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printBeforeLog(Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestLogger&nbsp;testLogger&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;description.getAnnotation(TestLogger.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(testLogger&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(description.getClassName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(description.getMethodName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(testLogger.log());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printAfterLog(Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(description.getClassName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(description.getMethodName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;end<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a&nbsp;simple&nbsp;division</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;simpleDivide()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(value&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test(expected&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ArithmeticException.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">divided&nbsp;by&nbsp;zero,&nbsp;and&nbsp;an&nbsp;ArithmeticException&nbsp;thrown.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;dividedByZero()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">值得注意的是，classRule是静态变量，它使用@ClassRule Annotation，将替代@BeforeClass和@AfterClass；而rule是成员变量，它使用@Rule Annotation，将替代@Before和@After。与<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html">之前文章</a>不同的是，此处不仅会在执行测试方法之前打印指定内容的日志(printBeforeLog())，还会在执行测试方法之后打印一条固定格式的日志(printAfterLog())，用于指示该测试方法已经执行完毕了。</span><br /><br /><strong><span style="font-size: 12pt;">3. 小结</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用Rule可以替代绝大部分的Runner扩展，而且特定的Rule实现可以被复用，也易于添加或移除Rule实例，这些都大大地提高了灵活性。值得注意地是，本文虽然使用Rule代替了@BeforeClass，@AfterClass，@Before和@After的功能，但并不意味着就应当这么做。就我个人所想，将传统的Fixture功能交由@BeforeClass，@AfterClass，@Before和@After实现，仍然是一种不错的选择。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/367802.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2012-01-04 00:13 <a href="http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索JUnit4扩展：应用Rule(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 24 Dec 2011 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/366801.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/366801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/366801.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">探索JUnit4扩展：使用Rule</span></strong></div>在上一篇文章<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">《探索JUnit4扩展：扩展Runner》</a>中，讨论了一种扩展JUnit4的方式，即，直接修改Test Runner的实现(BlockJUnit4ClassRunner)。但这种方法显然不便于灵活地添加或删除扩展功能。本文将使用JUnit4.7才开始引入的扩展方式--Rule来实现相同的扩展功能。(2010.12.25最后更新)<br /><br /><strong style="font-size: 12pt;">1. Rule</strong><br style="font-size: 12pt;" />Rule是JUnit4.7才开始提供的一种扩展方式，它能够替代大部分已有的Runner扩展。JUnit包含两种Rule Annotation：@ClassRule与@Rule。@ClassRule应用于测试类中的静态变量，而@Rule应用于成员变量；相同地是，这些变量必须是TestRule接口的实例，且访问修饰符必须为public。<br />在<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">上篇博文</a>中，对BlockJUnit4ClassRunner进行了扩展，被扩展的方法是methodBlock，现在我们来看看该方法体中的代码，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Statement&nbsp;methodBlock(FrameworkMethod&nbsp;method)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;test;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ReflectiveCallable()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Object&nbsp;runReflectiveCall()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;createTest();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}.run();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Throwable&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Fail(e);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;methodInvoker(method,&nbsp;test);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;possiblyExpectingExceptions(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withPotentialTimeout(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withBefores(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withAfters(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withRules(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;statement;<br />}</span></div>但在BlockJUnit4ClassRunner中，possiblyExpectingExceptions()，withPotentialTimeout()，withBefores()和withAfters()都已经被标注为过时，JUnit建议使用Rule来替代这些方法的功能。<br /><br /><strong style="font-size: 12pt;">2. TestLogRule</strong><br style="font-size: 12pt;" />如第1节所述，Rule Annotation要作用于TestRule接口的实例，那么就要先创建一个TestRule的实现类。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TestLogRule&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;TestRule&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DateFormat&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy-MM-dd_HH:mm:ss_SSS</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Statement&nbsp;apply(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestLogger&nbsp;testLogger&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;description.getAnnotation(TestLogger.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(testLogger&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(description.getClassName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(description.getMethodName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(testLogger.log());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;base;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>如上所示，TestLogRule与<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">上篇博文</a>中的LoggedRunner的代码有许多相同之处，功能则都是打印出指定的日志，每行日志又以当时的执行时间与完整方法名作为前缀。<br /><br /><strong><span style="font-size: 12pt;">3. 使用Rule的CalculatorTest</span></strong><br />下面是新的测试类CalculatorTest，它将不使用BlockJUnit4ClassRunner的扩展LoggedRunner作为测试执行器，所以该类没有使用@RunWith(LoggedRunner.class)，那么在执行该测试类时仍然会使用BlockJUnit4ClassRunner。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CalculatorTest&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Rule<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;TestLogRule&nbsp;testLogRule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;TestLogRule();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@BeforeClass<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;createCalculator()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Calculator();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a&nbsp;simple&nbsp;division</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;simpleDivide()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(value&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test(expected&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ArithmeticException.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">divided&nbsp;by&nbsp;zero,&nbsp;and&nbsp;an&nbsp;ArithmeticException&nbsp;thrown.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;dividedByZero()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>与<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">上篇博文</a>中的CalculatorTest相比，本文中的CalculatorTest除了没有使用LoggedRunner之外，还多了两行代码<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">@Rule<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;TestLogRule&nbsp;testLogRule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;TestLogRule();</span></div>在执行单元测试方法之前，BlockJUnit4ClassRunner会调用TestRule/TestLogRule中的apply()方法，即，会先打印出日志内容。<br /><br /><strong style="font-size: 12pt;">4. 小结</strong><br style="font-size: 12pt;" />使用Rule对JUnit进行扩展，能够避免对默认Runner的扩展，为测试类添加或移除Rule十分方便，而且Rule实现类本身也能很方便地被复用。在<a href="http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html">下一篇博文</a>中将进一步探索Rule的应用。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/366801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2011-12-24 23:26 <a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索JUnit4扩展：扩展Runner(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 13 Dec 2011 16:01:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/366289.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/366289.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/366289.html</trackback:ping><description><![CDATA[<div>
<div align="center"><strong style="font-size: 14pt;">探索JUnit4扩展：扩展Runner<br />
</strong></div>
在使用JUnit的过程中，大家可能会对JUnit进行一些扩展。本文中的示例为JUnit4定义了一个新的Annotation，并相应地对已有的Runner进行扩展，使其能够解析新引入的Annotation。(2011.12.25最后更新)<br />
<br />
本文臆造了一个示例，会在执行单元测试方法之前，自动地为单元测试方法打印日志。该示例会为JUnit定义一个新的Annotation用于指定要打印的日志内容，并对JUnit默认提供的Runner实现BlockJUnit4ClassRunner进行扩展，使其能够识别这个新的Annotation。<br />
<br />
<strong style="font-size: 12pt;">1. 定义Annotation</strong><br style="font-size: 12pt;" />
&nbsp;&nbsp;&nbsp; TestLogger是一个作用于方法的Annotation，它只有一个属性，用于指定日志的内容，其代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">@Target({&nbsp;ElementType.METHOD&nbsp;})<br />
@Retention(RetentionPolicy.RUNTIME)<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;@</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;TestLogger&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;log()&nbsp;</span><span style="color: #0000FF; ">default</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">""</span><span style="color: #000000; ">;<br />
}</span></div>
<br />
<strong><span style="font-size: 12pt;">2. 扩展Runner</span></strong><br />
&nbsp;&nbsp;&nbsp; JUnit提供了若干个Runner的实现，如BlockJUnit4ClassRunner，Suite，其中BlockJUnit4ClassRunner用来执行单个测试用例类。LoggedRunner将扩展BlockJUnit4ClassRunner，覆写其中的methodBlock()方法。新的methodBlock()方法会在一开始试图获取被执行测试方法中的TestLogger Annotation，如果存在的话，就会打印出指定的日志，每行日志以当时的执行时间与完整方法名作为前缀。该类的代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;LoggedRunner&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;BlockJUnit4ClassRunner&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DateFormat&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy-MM-dd_HH:mm:ss_SSS</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;LoggedRunner(Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;klass)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;InitializationError&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">(klass);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Statement&nbsp;methodBlock(FrameworkMethod&nbsp;method)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;classMethod&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;method.getMethod();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestLogger&nbsp;loggerAnnotation&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;classMethod.getAnnotation(TestLogger.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(loggerAnnotation&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(classMethod.getDeclaringClass().getName())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(classMethod.getName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(loggerAnnotation.log());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">.methodBlock(method);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<strong style="font-size: 12pt;">3. 应用程序</strong><br style="font-size: 12pt;" />
&nbsp;&nbsp;&nbsp; Calculator是一个简单的应用程序，其中定义了一个除法方法，代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;divide(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;a,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;b)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;b;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<strong><span style="font-size: 12pt;">4. 单元测试程序</span></strong><br />
&nbsp;&nbsp;&nbsp; CalculatorTest是一个简单的单元测试程序，它会使用两种方式对Calculator中的divide()方法进行单元测试。其代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">@RunWith(LoggedRunner.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CalculatorTest&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@BeforeClass<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;createCalculator()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Calculator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />
&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a&nbsp;simple&nbsp;division</span><span style="color: #000000; ">."</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;simpleDivide()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(value&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Test(expected&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ArithmeticException.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">divided&nbsp;by&nbsp;zero</span><span style="color: #000000; ">, and an ArithmeticException thrown."</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;dividedByZero()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
值得注意的是，CalculatorTest特别指定LoggedRunner作为测试执行器(@RunWith(LoggedRunner.class))；同时，每个单元测试方法，simpleDivide()与dividedByZero()，都使用了Annotation TestLogger，为其指定日志内容。当执行上述单元测试时，会自动地打印出如下形式的日志内容：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">2011</span><span style="color: #000000; ">-</span><span style="color: #000000; ">12</span><span style="color: #000000; ">-13_</span><span style="color: #000000; ">23</span><span style="color: #000000; ">:</span><span style="color: #000000; ">48</span><span style="color: #000000; ">:38_</span><span style="color: #000000; ">218</span><span style="color: #000000; "> test.CalculatorTest#simpleDivide:&nbsp;a&nbsp;simple&nbsp;division<br />
</span><span style="color: #000000; ">2011</span><span style="color: #000000; ">-</span><span style="color: #000000; ">12</span><span style="color: #000000; ">-13_</span><span style="color: #000000; ">23</span><span style="color: #000000; ">:</span><span style="color: #000000; ">48</span><span style="color: #000000; ">:38_</span><span style="color: #000000; ">218</span><span style="color: #000000; "> test.CalculatorTest#dividedByZero:&nbsp;divided&nbsp;by&nbsp;zero, and an ArithmeticException thrown.<br />
</span></div>
</div><br /><strong style="font-size: 12pt;">5. 小结</strong><br style="font-size: 12pt;" />通过对BlockJUnit4ClassRunner的扩展，可以让JUnit在运行测试用例时做一些额外的工作。但这种直接修改默认Test Runner的方式并不被提倡，在<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html">下一篇文章</a>中将会介绍使用Test Rule来达到相同的扩展目的。<br />&nbsp;<img src ="http://www.blogjava.net/jiangshachina/aggbug/366289.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2011-12-14 00:01 <a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>详解Eclipse断点(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/11/20/364367.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 20 Nov 2011 12:57:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/11/20/364367.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/364367.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/11/20/364367.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/364367.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/364367.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">详解Eclipse断点</span></strong></div><span style="font-size: 10pt;">大家肯定都用过Eclipse的调试的功能，在调试的过程中自然也无法避免要使用断点(breakpoint)，但不知是否对Eclipse中各类断点都有所了解。本篇图文并茂地介绍了Eclipse中全部类型的断点，及其设置，希望对大家有所帮助。(2011.11.20)</span><br /><br /><strong><span style="font-size: 12pt;">1. 示例程序</span></strong><br /><span style="font-size: 10pt;">BreakpointDemo是一个臆造的应用程序，只是为了便于讲解Eclipse中各类断点的使用罢了。其代码如下图所示，</span><br /><span style="font-size: 10pt;"><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/01.png" height="738" width="594" /></span><br /><span style="font-size: 10pt;">BreakpointDemo主要包含两个方法：</span><br /><span style="font-size: 10pt;">[1]setValue，该方法根据指定的次数(count)，对成员变量value进行赋值，值的范围为0-9的随机整数。</span><br /><span style="font-size: 10pt;">[2]printValue，该方法会调用setValue()对value进行赋值，并打印出value的值；但，如果value能被3整除，那么就会抛出IllegalArgumentException异常。</span><br /><br /><strong><span style="font-size: 12pt;">2. Line Breakpoint</span></strong><br /><span style="font-size: 10pt;">Line Breakpoin是最简单的Eclipse断点，只要双击某行代码对应的左侧栏，就对该行设置上断点。此处，对第20行代码设置上Line Breakpoint，如下图所示，</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/02.png" height="738" width="594" /><br /><span style="font-size: 10pt;">可以为Line Breakpoint设置一个条件，那么当程序运行到该断点时，只有满足设定的条件，才会被中断。右键点击第20行的断点，选择"Breakpoint Properties..."</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/03.PNG" height="738" width="594" /><br /><span style="font-size: 10pt;">在弹出的属性对话框中，勾选上"Conditional"，然后在文本框中输入"count % 2 == 0"。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/04.PNG" height="543" width="636" /><br /><span style="font-size: 10pt;">该条件表示，当程序运行到第20行时，只有当count为偶数时，程序才会被中断。细心地话，你会发现该断点的图标发生了改变，多了一个问号。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/04_01.PNG" height="738" width="594" /><br /><br /><strong><span style="font-size: 12pt;">3. Watchpoint</span></strong><br /><span style="font-size: 10pt;">Line Breakpoint关注于程序运行的"过程"，大家也常把使用这种断点的调试称为单步调试。但有时候，我们对程序的运行过程不太了解，可能也不太关心，不能确定在什么地方设置断点比较合适，而可能比较关注某个关键变量的变化或使用。此时，就可以为该变量设置一种特殊的断点--Watchpoint。在此示例，我们最关心的就是成员变量value的值，那么就可以为它设置一个Watchpoint，双击第9行代码对应的左侧栏就可以了。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/05.PNG" height="738" width="594" /><br /><span style="font-size: 10pt;">使用在2中所提及的方法，查看该断点的属性，</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/06.PNG" height="543" width="627" /><br /><span style="font-size: 10pt;">默认地，当该变量被访问或它的值被修改时，程序都会被中断。但在本示例中，只希望当对value的值进行修改时程序才需要被中断，所以取消对"Access"的勾选。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/07.PNG" height="543" width="627" /><br /><span style="font-size: 10pt;">这时，我们会发现原来的Watchpoin图标也有变化了。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/08.PNG" height="738" width="594" /><br /><br /><strong><span style="font-size: 12pt;">4. Method Breakpoint</span></strong><br /><span style="font-size: 10pt;">与关注对某个变量的访问与修改一样，我们也可以关注程序对某个方法的调用情况，即，可以设置Method Breakpoint。在此处，设置针对方法setValue的Method Breakpoint。同理，双击第11行代码对应的左侧栏即可。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/09.PNG" height="738" width="594" /><br /><span style="font-size: 10pt;">仍然要查看该断点的属性。默认地，只勾选了"Entry"，而没有勾选"Exit"。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/10.PNG" height="543" width="695" /><br /><span style="font-size: 10pt;">这表示，当刚进入该方法(调用开始)时，程序会被中断；而，离开该方法(调用结束)时，程序并不会被中断。在本示例中，需要同时勾选上"Exit"。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/11.PNG" height="543" width="695" /><br /><span style="font-size: 10pt;">点击OK之后，可以看到该断点的图标也有所改变。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/12.PNG" height="738" width="594" /><br /><span style="font-size: 10pt;">根据这里的设置，当程序运行到第20行后会在第12行被中断，尽管这里没有显式的断点，但这就是setValue()方法的入口(Entry)。必须注意地是，程序在运行到第16行时不会被中断，尽管它看起来像是setValue()方法的出口(Exit)。实际上，程序会在第17行被中断，这里才是setValue()调用结束的地方。</span><br /><br /><strong><span style="font-size: 12pt;">5. Exception Breakpoint</span></strong><br /><span style="font-size: 10pt;">如果，我们期望某个特定异常发生时程序能够被中断，以方便查看当时程序所处的状态。通过设置Exception Breakpoint就能达到这一目标。本示例故意在第23行抛出了IllegalArgumentException异常，我们期望程序运行到此处时会被中断。但我们不直接为此行代码设置Line Breakpoint，而是为IllegalArgumentException设置Exception Breakpoint。设置Exception Breakpoint的方法与其它类型断点都不同，它不能通过双击左侧栏的方式在代码编辑器上直接进行设置。点击Breakpoints视图右上角形如Ji的图标，</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/17.PNG" height="160" width="229" /><br /><span style="font-size: 10pt;">会弹出如下所示的对话框，</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/14.PNG" height="500" width="600" /><br /><span style="font-size: 10pt;">在其中选中IllegalArgumentException，并点击OK，这时一个Exception Breakpoint就设置好了。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/15.PNG" height="738" width="594" /><br /><div><span style="font-size: 10pt;">当value为3的倍数时，程序会在第23行被中断，这时我们就能使用调试器来看看value具体是等于0，3或6。</span></div><br /><strong><span style="font-size: 12pt;">6. Class Load Breakpoint</span></strong><br /><span style="font-size: 10pt;">还有一种大家平时可能不太用的断点--Class Load Breakpoint，即当某个类被加载时，通过该断点可以中断程序。</span><br /><span style="font-size: 10pt;"><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/ec_breakpoints/16.PNG" height="738" width="594" /><br /><br /></span><span style="font-size: 12pt;"><strong>小结</strong></span><span style="font-size: 10pt;"><br />上述的Eclipse断点，我们在现实工作中肯定都有意或无意地使用过其中的几种，只是不一定十分了解内情罢了。使用好Eclipse的各种断点，可以把很好地帮助我们分析程序，定位问题。<br /><br /></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/364367.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2011-11-20 20:57 <a href="http://www.blogjava.net/jiangshachina/archive/2011/11/20/364367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构建不规则窗体(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/05/31/351369.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 31 May 2011 12:46:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/05/31/351369.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/351369.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/05/31/351369.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/351369.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/351369.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div>
<div align="center"><strong><span style="font-size: 14pt;">构建不规则窗体</span></strong></div>
<span style="font-size: 10pt;">
<div>在开发一个新浪微博Swing客户端的过程中希望能展现不规则的窗体界面，原来<a href="http://www.oracle.com/technetwork/java/javase/6u10-142936.html">JDK 6 update 10</a>提供了创建指定形状窗体的特性，简单易用，记于此处。(2010.05.31最后更新)</div>
<br />
Java从<a href="http://www.oracle.com/technetwork/java/javase/6u10-142936.html">JDK 6 update 10</a>开始将内建支持构建指定形状的窗体，类com.sun.awt.AWTUtilities中的方法setWindowShape会根据不同的Shape实现去构造相应形状的窗体。AWTUtilities类是放在SUN的包中，在使用该方法时应该通过反射去进行调用，如下代码所示，</span></div>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;clazz&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Class.forName(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">com.sun.awt.AWTUtilities</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
Method&nbsp;method&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;clazz.getMethod(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">setWindowShape</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;Window.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">,&nbsp;Shape.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);</span></div>
<span style="font-size: 10pt;"><br />
</span><strong><span style="font-size: 12pt;">1. 创建正常窗体
</span></strong><span style="font-size: 10pt;"><br />先创建一个简单的界面，它使用BorderLayout，在其中安放5个JButton，如下代码所示，<br /></span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;ShapedFrame&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;JFrame&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2291343874280454383L</span><span style="color: #000000; ">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;JButton&nbsp;centerButton&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Center</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;ShapedFrame()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Shaped&nbsp;Frame</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;initUI()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Container&nbsp;container&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;getContentPane();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.setLayout(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BorderLayout());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">TOP</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.PAGE_START);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">RIGHT</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.LINE_END);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">BOTTOM</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.PAGE_END);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">LEFT</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.LINE_START);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(centerButton,&nbsp;BorderLayout.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SwingUtilities.invokeLater(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Runnable()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShapedFrame&nbsp;frame&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ShapedFrame();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setSize(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Dimension(</span><span style="color: #000000; ">400</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">300</span><span style="color: #000000; ">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setUndecorated(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAtCenter(frame);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setVisible(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;将Window置于屏幕正中</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setAtCenter(Window&nbsp;window)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dimension&nbsp;screenSize&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Toolkit.getDefaultToolkit().getScreenSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.setLocation((screenSize.width&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;window.getWidth())&nbsp;</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(screenSize.height&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;window.getHeight())&nbsp;</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div><br />执行上述程序的效果如下图所示，<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/01.PNG" height="525" width="799" /><br /><br /><span style="font-size: 10pt;">
</span><strong><span style="font-size: 12pt;">2. 创建不规则窗体
</span></strong><span style="font-size: 10pt;"><br />基于上述程序创建不规则窗体，使整个窗体正好缺失掉RIGHT JButton所在的区域，如下代码所示，<br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;ShapedFrame&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;JFrame&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2291343874280454383L</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Method&nbsp;method&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;clazz&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Class.forName(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">com.sun.awt.AWTUtilities</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;method&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;clazz.getMethod(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">setWindowShape</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;Window.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">,&nbsp;Shape.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Exception&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;JButton&nbsp;centerButton&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Center</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;ShapedFrame()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Shaped&nbsp;Frame</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addComponentListener(componentListener);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;initUI()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Container&nbsp;container&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;getContentPane();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.setLayout(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BorderLayout());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">TOP</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.PAGE_START);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">RIGHT</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.LINE_END);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">BOTTOM</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.PAGE_END);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;JButton(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">LEFT</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;BorderLayout.LINE_START);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(centerButton,&nbsp;BorderLayout.CENTER);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;ComponentListener&nbsp;componentListener&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ComponentAdapter()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;componentResized(ComponentEvent&nbsp;evt)&nbsp;{&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当UI组件(JFrame)的尺寸发生改变时，调用该方法</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rectangle&nbsp;frameRect&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;getBounds();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rectangle&nbsp;spaceRect&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;centerButton.getBounds();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;o1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;o2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(frameRect.width,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;o3&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(frameRect.width,&nbsp;frameRect.height);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;o4&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;frameRect.height);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;i1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(spaceRect.x&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;spaceRect.width,&nbsp;spaceRect.y);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;i2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(frameRect.width,&nbsp;spaceRect.y);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;i3&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(frameRect.width,&nbsp;spaceRect.y<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;spaceRect.height);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;i4&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Point(spaceRect.x&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;spaceRect.width,&nbsp;spaceRect.y&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;spaceRect.height);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">[]&nbsp;xpoints&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">[]&nbsp;{&nbsp;o1.x,&nbsp;o2.x,&nbsp;i2.x,&nbsp;i1.x,&nbsp;i4.x,&nbsp;i3.x,&nbsp;o3.x,&nbsp;o4.x&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">[]&nbsp;ypoints&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">[]&nbsp;{&nbsp;o1.y,&nbsp;o2.y,&nbsp;i2.y,&nbsp;i1.y,&nbsp;i4.y,&nbsp;i3.y,&nbsp;o3.y,&nbsp;o4.y&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;npoints&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">8</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;构建一个六边形，将RIGHT&nbsp;JButton所处的位置空缺出来</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Shape&nbsp;shape&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Polygon(xpoints,&nbsp;ypoints,&nbsp;npoints);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setWindowShape(ShapedFrame.</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">,&nbsp;shape);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;设置Window的形状</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setWindowShape(Window&nbsp;window,&nbsp;Shape&nbsp;shape)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;method.invoke(</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">,&nbsp;window,&nbsp;shape);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Exception&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SwingUtilities.invokeLater(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Runnable()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShapedFrame&nbsp;frame&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ShapedFrame();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setSize(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Dimension(</span><span style="color: #000000; ">400</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">300</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setUndecorated(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAtCenter(frame);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setVisible(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;将Window置于屏幕正中</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setAtCenter(Window&nbsp;window)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dimension&nbsp;screenSize&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Toolkit.getDefaultToolkit().getScreenSize();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.setLocation((screenSize.width&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;window.getWidth())&nbsp;</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(screenSize.height&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;window.getHeight())&nbsp;</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />
<span style="font-size: 10pt;">执行上述程序后，会有如下图所示的效果，<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/02.PNG" height="524" width="799" /><br />
<br />
</span><span style="font-size: 10pt;"></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/351369.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2011-05-31 20:46 <a href="http://www.blogjava.net/jiangshachina/archive/2011/05/31/351369.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaOne and Oracle Develop 2010(Beijing)--Day 2(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Thu, 16 Dec 2010 12:52:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/340835.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/340835.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/340835.html</trackback:ping><description><![CDATA[<br />
<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">JavaOne and Oracle Develop 2010(Beijing)--Day 2</span></strong></span><br />
</div>
<span style="font-size: 10pt;">本文是我参加<a href="http://www.oracle.com/cn/javaonedevelop/index.html">JavaOne与Oracle开发者大会(北京)</a>第二天活动的一些见闻与感受，有图有真相，希望大家能喜欢。(2010.12.16最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; Day 2？是的，由于我周一比较忙，就没有参加Day 1的活动，所以Day 2才开始参会。不过Day 1的活动是下午才开始的，而且主要与开幕相关，技术专题都还没开始，所以是否参加都无所谓。<br />
<br />
<strong><span style="font-size: 12pt;">1. 赴会</span></strong><br />
&nbsp;&nbsp;&nbsp; 周一确实很忙，加班到半夜，一点钟才回到家中，洗漱完毕后又看了会儿电视，二点钟才睡下。清晨六点，闹钟将我吵醒，挣扎了十分钟后，我还是翻身起床了...吃完了老妈煮的排骨面，我出门了。北京最近正在降温，清早的温度更低，还好穿了一件较厚的羽绒服，呼吸着冰冷的空气，我的精神还不错。<br />
&nbsp;&nbsp;&nbsp; 我住在北五环外闻名京城的某大型"人文"社区，去会议现场--<a href="http://www.cnccchina.com/">国家会议中心</a>，全程只需要坐城铁/地铁就可以了，交通还算方便。但我遇上了早高峰(我平时是坐公司班车，不用挤城铁)，经历了一番艰难险阻(此处省去100万字)之后，我终于"被"挤进了城铁的车厢...又转了两次地铁之后，终于到了奥林匹克公园站，出站后再走上几步就到了此行的目的地--<a href="http://www.cnccchina.com/">国家会议中心</a>。<br />
<span style="font-size: 12pt;"><strong><br />
2. 会场印象</strong></span><strong></strong><br />
&nbsp;&nbsp;&nbsp; 为了防止恐怖分子，进会场之前要安检，包包和身体都被检测了一番。在工作人员的引导下，我进行了会议注册。举办方为每个参会者发了一个黑色的背包，里面装有会议指南，笔记本，水性笔，还有一个水杯。我觉得笔和水杯都太次了;-) 值得提及的是，我在参会之前的抽奖活动中赢得了一个2G版的<a href="http://www.apple.com.cn/ipodshuffle/">iPod Shuffle</a>，参加了多次技术会议，还是第一次中奖:-D<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/ipod.jpg" height="450" width="600" /><br />
<br />
&nbsp;&nbsp;&nbsp; 甫一进入会场，我就感到Oracle的手笔比Sun大多了(不过，咱交的<a href="http://www.oracle.com/cn/javaonedevelop/javaone-register-cn-161000-zhs.html">会费</a>也多了好几倍呢)。整个会场很大，但会场内各处的工作人员也很多，考虑得还是比较贴心的。由于是第一次到国家会议中心，对它的布局一点儿都不熟悉，看着展板中各个主题或技术专题会场的房间代码，我很是有点儿晕，所以举办方设置了多个咨询处，当然，我也可以随时咨询身边的工作人员。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/hall_01.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/hall_02.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/hall_03.jpg" alt="" border="0" height="450" width="600" /><br />
<br />
<strong><span style="font-size: 12pt;">3. 主题演讲/技术专题</span></strong><br />
&nbsp;&nbsp;&nbsp; 我上午主要就是在大会堂(比人民大会堂小多了)里听了两场主题演讲。首先是Oracle的两位VP，分别介绍了Oracle的最新技术更新情况，软硬件均有涉及。从演讲的内容来看，云计算是重中之重，即所谓的<a href="http://www.oracle.com/us/products/middleware/exalogic/index.html">Exalogic Elastic Cloud Computing</a>，还特别推荐了<a href="http://www.oracle.com/us/products/database/exadata/index.html">Exadata存储服务器</a>，应该也与云计算有关。第二场演讲是神州数码(God，神马就是浮云？！)的老总作的关于数据城市的主题。不得不说，国内老总的官派作风严重，我本来对这个题目就没兴趣，结果这位老总说得又慢又没劲，我没听完就走了。<br />
&nbsp;&nbsp;&nbsp; 下午我仍然听了几场主题演讲，首先是，Oracle软件部的一位高级总监介绍了Java的发展状况。按照以前参加SunTechDays的经验，一般都会有类似的主题。主要就是介绍Java当前有哪些主要的项目，各个项目的发展前景如何，会有哪些特性，计划在什么时候发布。我印象比较深的主要是<a href="http://openjdk.java.net/">OpenJDK</a>，以及其中的<a href="http://openjdk.java.net/projects/coin/">Coin项目</a>，它是关于一些小的Java语言级变化，为了提高劳动生产率。还有<a href="http://openjdk.java.net/projects/lambda/">Lambda项目</a>，它是关于闭包的。原计划闭包会出现在JDK 7中，但现在只可能出现在JDK 8中了。<br />
&nbsp;&nbsp;&nbsp; 听完这个主题演讲后，我就去听了一个技术专题--使用Oracle数据库构建高性能应用程序的最佳实践。我没听到开头，讲师是一位老黑，不知原本是哪国人，英语讲得很蹩脚。我现在只记得几个技术点，大约是说使用连接池，不要用绑定变量，还要使用某种Cache，这样就能提高性能。讲师对自己都很不满意，他在结束时还不好意思地笑着问大家有没有听懂他在说什么。哈哈，还是满可爱的嘛:-D<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/inner_00.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/inner_03.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/inner_05.jpg" alt="" border="0" /><br />
<br />
<strong><span style="font-size: 12pt;">4. 展厅</span></strong><br />
&nbsp;&nbsp;&nbsp; 会场内有一个展示大厅，里面设有Oracle，以及各个会议赞助商的展台。一片嘈杂的景象，我闲逛了一番，但没找到自己感兴趣的东西，只是在其中的网吧里上了会儿网，向公司里的同事发了几封邮件。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/pavilion_01.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/pavilion_02.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/pavilion_03.jpg" alt="" border="0" /><br />
<strong><span style="font-size: 12pt;"><br />
5. 午餐</span></strong><br />
&nbsp;&nbsp;&nbsp; 举办方为每位参会者准备了免费的午餐(世界上真有免费的午餐吗？)，我们凭每天特定的餐劵进入餐厅吃饭。餐厅很大，可同时容纳数以千记的人吃饭。午餐是自助餐，不得不说，交得会费多，午餐也就会丰盛些。想想以前参加SunTechDays时，午餐就是一份盒饭，既吃不饱，也吃不好。自助餐里中式菜的味道一般，特别是羊排完全没煮烂，每咬一口都要费很大力(绝不夸张)。还好我拿得不多，勉强吃完了，然后又拿了一些水果和甜点，这些还不错。另有咖啡和汤，我就没拿了，不想吃太多。总之，我对午餐表示满意(我这人很好养活的)。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/2010_02_food_01.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/2010_02_food_02.jpg" alt="" border="0" height="450" width="600" /><br />
<br />
<strong><span style="font-size: 12pt;">请关注我的系列博文--<a href="http://www.blogjava.net/jiangshachina/category/27188.html">JavaOne/SunTechDays</a></span></strong><br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/340835.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2010-12-16 20:52 <a href="http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaOne and Oracle Develop 2010 (Beijing)--Preview(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 12 Dec 2010 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/340183.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/340183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/340183.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;">
<span style="font-size: 14pt;">JavaOne and Oracle Develop 2010 (Beijing)--Preview</span></span></strong><br />
</div>
<span style="font-size: 10pt;">
自从<a href="http://www.oracle.com/us/corporate/press/018363">Sun被Oracle收购</a>之后，往常一年一度的SunTechDays就离我们而去了，但它以另一种形式--<a href="http://www.oracle.com/cn/javaonedevelop/index.html">JavaOne与Oracle开发者大会(北京)</a>--回到了我们的身边。本次大会即将在下周开幕，在此之前请允许我先列举一下我个人感兴趣的若干主题。(2010.12.12最后更新)<br />
<br />
<a href="http://www.oracle.com/cn/javaonedevelop/index.html">JavaOne与Oracle开发者大会2010(北京)</a>下周就要与大家见面了，其实与本次会议同时进行的还有Oracle全球大会2010(北京)(大家肯定知道Oracle的<a href="http://www.oracle.com/us/openworld/splash/index.html">Openworld</a>吧)。这两个会议相对独立，可以同时注册，也可以只参加其中任何一个，当然，参会费将有所不同。由于我只参加JavaOne与Oracle开发者大会2010(北京)，故本文中的内容将只涉及这一会议中的相关主题。各个主题的的<a href="https://oraclecn.wingateweb.com/scheduler/eventcatalog/eventCatalog.do">简介</a>我均摘抄自本次会议的官方网站，最后我还会混说几句^_^<br />
<br />
<strong><span style="font-size: 12pt;">1. Java程序设计语言</span></strong><br />
<strong>Coin项目：针对JDK的微小语言变化</strong><br />
&nbsp;&nbsp;&nbsp; <a href="http://openjdk.java.net/projects/coin/">Coin项目</a>旨在通过选择和实施一组微小的语言变化来提高程序员的JDK生产力。Coin项目语言变化包括改进的整数值、字符串转换和尖括号输入。本专题讲座将介绍这些语言变化并演示IDE支持。此外还将讨论通用语言演变的选择过程和标准。<br />
<br />
<strong>JDK 7和Java SE 7</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将展示Oracle工程师和外部人员为JDK 7和Java SE 7开发的一些主要特性。然后将展望一下未来版本的可能特性。<br />
<br />
<strong>在JDK中使用文件系统API</strong><br />
&nbsp;&nbsp;&nbsp; JDK不久将包含一个新的文件系统API，用于解决许多长期存在的问题和java.io.File局限性问题。本专题讲座将介绍该API并说明它可以如何用于：* 访问文件* 控制路径* 复制和移动文件* 处理符号链接* 执行递归操作* 访问文件权限和其他属性* 监控文件系统* 开发自己的定制文件系统* 其他我们将通过实例来说明如何有效使用该API来构建广泛使用文件系统的工具和应用程序。<br />
<br />
<strong>代码签名最佳实践</strong><br />
&nbsp;&nbsp;&nbsp; 代码签名对于开发人员来说极具吸引力，因为它能为Java应用或扩展提供通常没有的更多权限。然而，有时最佳的决策却是不用代码签名，因为这会改善用户体验，并且应用程序可在安全沙盒中自动运行。 本专题讲座将介绍代码签名最佳实践，探讨何时进行代码签名并提供其部署技巧。我们还将介绍使用户能够创建更引人注目的非签名应用程序的新特性以及即将推出的与代码签名相关的JDK特性，例如签名模块。<br />
<br />
<strong><span style="font-size: 12pt;">2. Java虚拟机</span></strong><br />
<strong>Oracle的Java虚拟机战略</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座介绍HotSpot和Oracle JRockit这两个通用的Oracle Java虚拟机 (JVM)。讲座将讨论两种实施的优缺点，给出选择建议&#8212;&#8212;特别是采用了Oracle融合中间件产品时，并介绍融合为一个统一JVM的路线图。<br />
<br />
<strong>我的应用程序怎么了：Java虚拟机监控工具</strong><br />
&nbsp;&nbsp;&nbsp; 随着多核系统的广泛使用，开发人员面临着新的问题，如CPU数量增加时性能下降。这些大规模问题往往是由于使用传统调试器和分析器时对局部事件的不可见（缓存丢失、锁定模式等）造成的。本专题讲座将介绍一个新的开发工具，它准确记录并巧妙显示线程、锁、处理器和内存之间的交互。演示将展示如何：* 监控JDK 6、JDK 7和Java实时系统* 使用分布视图来快速识别风险* 在出现特定问题时使用频率视图来探查* 使用调度视图来检查当时究竟发生了什么* 关联这些视图。<br />
<br />
</span><span style="font-size: 10pt;"><strong>垃圾回收流言终结者</strong><br />
&nbsp;&nbsp;&nbsp; 垃圾回收 (GC)
的采用促进了大型企业管理软件可靠性的提高和开发时间的缩短。但GC不是一个魔杖；尽管它可以消除许多种可能的程序员错误，但还有一些其他问题开发人员必
须加以注意在本专题讲座中，我们将对许多GC神话做一个公正的评估：它擅长什么和不擅长什么。尽管本讲座不会直接涉及某一特定产品，但它也值得所有使用
Java及依赖于Java虚拟机的其他语言（如Scala、Jython、Jruby等）编写程序并使用GC的程序员关注。<br />
<strong><br />
如何在Java虚拟机上调优和编写低延迟应用程序</strong><br />
&nbsp;&nbsp;&nbsp; 许多软件应用程序都需要低延迟响应。这些应用程序的许多使用者都希望利用Java SE所提供的开发和部署优势。随着Java虚拟机 (JVM)
的最新发展，许多有低延迟要求的应用程序都可以使用Java
SE来实施。本专题讲座将介绍如何针对这些应用程序和编程结构调优现代JVM，以及可实现低延迟响应的Java SE数据结构最佳实践。</span><br />
<span style="font-size: 10pt;">
<br />
<strong><span style="font-size: 12pt;">3. Oracle</span></strong><br />
<strong>使用Oracle数据库构建高性能应用程序的最佳实践</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将探讨使用Oracle数据库11g构建高性能应用的设计和编码最佳实践，并将涉及数据库访问层。本讲座还将演示Oracle数据库11g工具，如AWR和ADDM，突出它们所提供的卓越的应用程序性能洞察力，并展示这些工具如何在开发周期初期以及应用程序部署阶段帮助识别应用程序的问题。虽然介绍的是Java和C/C++用例，但所述技术还适用于其他语言，如PHP、Ruby、Python和Perl。本专题讲座适合于希望了解如何提升基于数据库的应用程序性能的任何人，无论他们使用何种编程语言。<br />
<br />
<strong>部署一个"隐形"的数据库：Oracle数据库11g第2版</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将详细介绍如何在嵌入式环境中部署具有行业优势的完整数据库。您将了解到如何使用Oracle数据库11g第2版目前提供的可用工具和功能将该数据库嵌入您的应用程序中，使之对最终用户完全透明。本专题讲座还将探讨嵌入式数据库的静默安装、管理和可用性。<br />
<br />
<strong>面向开发人员和DBA的强大Oracle SQL Developer报表</strong><br />
&nbsp;&nbsp;&nbsp; 大多数数据库管理员都有数据库健康检查和查看数据库状态的脚本，而应用开发人员和数据库开发人员则通过运行报表来监控其数据状况和检查其所用的数据库结构。Oracle SQL Developer为检查和查询实例及系统数据提供了一组现成报表。通过应用额外的过滤器，并进行相应的数据排序，可以审查和调整数据展现表格。本专题讲座将向用户展示如何利用这些报表和支持数据表格的特性。您将了解如何自建报表，包括图形化显示、主从报表、传递参数和钻取功能。<br />
<br />
<strong>使用内存数据库技术的高性能SQL应用程序</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将就Oracle TimesTen内存数据库和Oracle内存数据库高速缓存的产品功能进行技术讨论和演示。了解如何使用JDBC、ODBC、.NET、OCI、 Pro*C和PL/SQL等标准SQL和数据库API来显著提高应用程序响应速度和吞吐量。本专题讲座将揭示如何使用内存数据库高速缓存网格有效扩展应用程序，从而扩大事务吞吐量和高速缓存数据的有效尺寸。对于需要利用易于理解和广泛使用的数据库API来最大限度地提高新应用程序和现有应用程序性能的数据库开发人员，本专题讲座是最佳选择。<br />
<br />
<strong>使用内存数据库技术的高性能SQL应用程序</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将就Oracle TimesTen内存数据库和Oracle内存数据库高速缓存的产品功能进行技术讨论和演示。了解如何使用JDBC、ODBC、.NET、OCI、 Pro*C和PL/SQL等标准SQL和数据库API来显著提高应用程序响应速度和吞吐量。本专题讲座将揭示如何使用内存数据库高速缓存网格有效扩展应用程序，从而扩大事务吞吐量和高速缓存数据的有效尺寸。对于需要利用易于理解和广泛使用的数据库API来最大限度地提高新应用程序和现有应用程序性能的数据库开发人员，本专题讲座是最佳选择。<br />
<br />
<strong><span style="font-size: 12pt;">4. MySQL</span></strong><br />
<strong>MySQL 5.5版中性能和可伸缩性的增强 </strong><br />
&nbsp;&nbsp;&nbsp; 本技术专题讲座深入探讨了MySQL服务器和InnoDB 1.1插件中新增的性能特征。其中包括对多核系统获得的可伸缩性的详细介绍。<br />
<br />
<strong>MySQL性能调优最佳实践</strong><br />
&nbsp;&nbsp;&nbsp; 了解如何只利用MySQL附带的工具进行MySQL的基本调优。了解如何对连接进行调优以及选择最佳存储引擎，了解如何分析MySQL服务器的关键统计数据。本专题讲座是一个技术讲座，但无需具备MySQL经验。<br />
<br />
<strong>MySQL的战略：下一步计划是什么？</strong><br />
&nbsp;&nbsp;&nbsp; 本讲座将介绍MYSQL社区、产品和战略的最新信息，探讨MYSQL如何与Oracle全面、开放且集成的战略相一致。结识领先的MySQL开发者，了解MySQL的下一步开发计划。<br />
&nbsp;&nbsp;&nbsp; <em>笔者：由于Sun被Oracle收购后，MySQL就很自然地落入了Oracle的袋中，从那时起，整个社区对MySQL的前景就执悲观态度。MySQL算是我最熟悉的主流数据库了，但也很长时间没太关注了，这次就顺便听听Oracle将如何安排MySQL的命运。</em><br />
<br />
<strong><span style="font-size: 12pt;">5. Embedded/Mobile</span></strong><br />
<strong>我的朋友在哪里：Java ME定位API实践</strong><br />
&nbsp;&nbsp;&nbsp; 在本上机操作中，参会者将创建一个使用Java平台微型版(Java ME)定位API的移动应用程序，以跟踪其朋友的位置并在地图上显示。此外，该移动应用程序还支持朋友之间的文本消息传递，并会在地图上显示他们以及他们的照片和名字。本专题讲座适合希望了解如何将应用程序的内容与应用程序用户的当前位置进行捆绑的开发人员。参会者将了解：- 如何使用Java ME定位API- 如何在Java ME应用程序中使用谷歌地图服务- 如何使用Java ME通用连接框架- 如何使用可视化工具来设计应用程序的流和UI。<br />
</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <em>笔者：我一直都没关注过嵌入式或移动类应用，但现在这样的应用越来越普遍了，身边不少同事与朋友也在使用iPhone，或基于<a href="http://www.android.com/">Android</a>的智能手机，这都显示出手机应用的巨大市场潜力。而基于位置的移动应用也是方兴未艾(玩过<a href="http://foursquare.com/">Foursquare</a>吗？)，所以我这次也想去试试水。不过遗憾的是，这次没有<a href="http://www.android.com/">Google Android</a>的相关主题，难倒还是由于Oracle对Google的侵权诉讼？！大家还是应以和为贵。</em></span><br />
<span style="font-size: 10pt;">
<br />
<strong><span style="font-size: 12pt;">6. 杂想</span></strong><br />
&nbsp;&nbsp;&nbsp; 当年Sun被Oracle收购时，我还颇有几分感慨。无论是Java程序设计语言，还是<a href="http://www.sparc.org/">SPARC</a>处理器，以及<a href="http://hub.opensolaris.org/bin/view/Main/">Solaris/OpenSolaris</a>，在技术上都是同类中的佼佼者，但在商业上都未能成功。在软件方面，作为Java的创造者，Sun从Java中获得的收益不如IBM，还不得不开源了它的主要软件产品(如<a href="http://hub.opensolaris.org/bin/view/Main/">Solaris</a>，<a href="http://www.netbeans.org/">NetBeans</a>)，甚至于Java也被迫开源了--<a href="http://openjdk.java.net/">OpenJDK</a>。而在硬件方面，Sun的服务器，SPARC处理器也是曲高和寡，市场占用率很低。<br />
&nbsp;&nbsp;&nbsp; 此外，对Java发展有重要贡献的人物，如<a href="http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683">Effective Java</a>的作者<a href="http://en.wikipedia.org/wiki/Joshua_Bloch">Joshua Bloch</a>，Java之父<a href="http://nighthacks.com/roller/jag/">Games Gosling</a>，也相继离开了Sun/Oracle。回想当年，我第一次参加SunTechDays(JavaChina 2005)时，还有幸与Games Gosling有一张合影。可惜，在Oracle举办的会议中也许就再也看不到这位和蔼的大胡子老爹了;-(<br />
<br />
&nbsp;&nbsp;&nbsp; 由于工作原因，我不会参加明天(2010.12.13)下午的开幕式，不过明天下午的专题本来也很少，所以不参加也关系不大。我会为每一天的参会经历写一篇博文，记录自己的所见，所闻，所思，所感，还会附上图片，请大家持续关注。<br />
<strong><span style="font-size: 12pt;"><br />
请关注我的系列博文--<a href="http://www.blogjava.net/jiangshachina/category/27188.html">JavaOne/SunTechDays</a>。</span></strong><br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/340183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2010-12-12 20:55 <a href="http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据加载模糊进度指示面板的实现与应用(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 29 Nov 2009 12:33:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/304120.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/304120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/304120.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">数据加载模糊进度指示面板的实现与应用</span></span></strong><br />
</div>
<span style="font-size: 10pt;">
当在加载数据(或其它耗时工作)时，需要显示一个进度指示面板，本文介绍了一种简易的实现方式。(2009.11.30最后更新)<br />
<br />
对于许多Swing应用，在与用户的交互过程中可能需要与数据库进行通信(如，加载数据)。而这个过程往往比较耗时，为了不造成"假死"现象，一般都会显示一个模糊进度指示器(不一定使用<a href="http://java.sun.com/javase/7/docs/api/javax/swing/JScrollBar.html">JProgressBar</a>，简单地用一个图片代替即可)，当数据加载完毕后，该进度指示器自动消失。<br />
&nbsp;&nbsp;&nbsp; 一般地，该模糊进度指示器不会展示在一个弹出的对话框中(因为这样不美观)，而是直接显示在需要展示被加载数据的面板中，并且对该面板进行模糊处理。实现这一功能的关键就在于，在屏幕的同一区域内展示两层面板：一层是展示数据的面板；另一层是展示进度指示器的面板。当加载数据时，显示进度指示器面板，并模糊数据面板；当数据加载完毕后，隐藏进度指示器面板，并使数据面板清晰显示。下面将使用<a href="http://swinglabs.org/hudson/job/SwingX%20Weekly%20Build/javadoc/org/jdesktop/swingx/StackLayout.html">org.jdesktop.swingx.StackLayout</a>方式来实现上述功能。<br />
<br />
</span><span style="font-size: 10pt;"><strong><span style="font-size: 12pt;">1. LoadingPanel--加载指示器面板</span></strong><br />
&nbsp;&nbsp;&nbsp; 首先创建一个加载指示器面板。如前所述，我们不必使用真正的进度条作为进度指示器，仅需要使用一张动态图片来代替即可。LoadingPanel的完整代码如下所示，</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;LoadingPanel&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JPanel&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1962748329465603630L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;mesg&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;LoadingPanel(String&nbsp;mesg)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.mesg&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;mesg;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interceptInput();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setOpaque(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setVisible(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;initUI()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JLabel&nbsp;label&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JLabel(mesg);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label.setHorizontalAlignment(JLabel.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label.setIcon(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ImageIcon(getClass().getResource(</span><span style="color: #000000;">"</span><span style="color: #000000;">/path/to/spinner.gif</span><span style="color: #000000;">"</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setLayout(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add(label,&nbsp;BorderLayout.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;"> interceptInput()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseAdapter()&nbsp;{});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseMotionListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseMotionAdapter()&nbsp;{});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addKeyListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;KeyAdapter()&nbsp;{});<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addComponentListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ComponentAdapter()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;componentShown(ComponentEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestFocusInWindow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setFocusTraversalKeysEnabled(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">
上述代码很容易理解，LoadingPanel中仅有一个JLabel，它会展示一张图片(spinner.gif)及一段信息。但有两段代码需要特别说明：<br />
[1]构造器中的两行代码</span><br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">setVisible(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
setOpaque(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);</span><br />
</div>
<span style="font-size: 10pt;">
LoadingPanel只在加载数据时才显示，其它时候是不显示的，所以它默认不可见。另外，在显示LoadingPanel的同时，我们仍然希望能看到数据面板，所以LoadingPanel应该是透明的。<br />
[2]interceptInput方法<br />
当LoadingPanel显示之后，我们不希望用户还能够操作数据面板，那么就需要屏蔽掉用户(鼠标，键盘)输入。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">addMouseListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseAdapter()&nbsp;{});<br />
addMouseMotionListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseMotionAdapter()&nbsp;{});<br />
addKeyListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;KeyAdapter()&nbsp;{});</span></div>
<span style="font-size: 10pt;">上述三行代码就使得LoadingPanel能捕获所有的鼠标与键盘事件，并忽略掉它们。但仅仅如此还不够，在展示LoadingPanel时，数据面板中的某个UI组件很可能已经获得焦点了，那么用户仍然可以通过键盘操控数据面板中的组件(因为系统会把键盘事件发送给当前获取焦点的组件)。而且，即使数据面板中没有任何组件获得焦点，用户仍然可以通过Tab键把焦点转移到数据面板中的组件上。为了阻止这一操作，还需要加上如下几行代码，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">addComponentListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ComponentAdapter()&nbsp;{&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;一旦LoadingPanel可见，即获取焦点</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;componentShown(ComponentEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestFocusInWindow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
});<br />
setFocusTraversalKeysEnabled(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;阻止用户转移焦点</span></div>
<span style="font-size: 10pt;"><strong><span style="font-size: 12pt;"><br />
2. 示例程序</span></strong><br />
&nbsp;&nbsp;&nbsp; 在此处的示例程序中，数据面板(dataPanel)中仅有一个按钮，当点击该按钮时会显示loadingPanel，且模糊掉dataPanel，并会启动一个新的线程，该线程会在睡眠大约3秒(模拟耗时的数据加载工作)之后隐藏loadingPanel，且使dataPanel重新清晰可见。<br />
&nbsp;&nbsp;&nbsp; 值得注意的是，该示例程序使用了<a href="https://swingx.dev.java.net/">SwingX</a>中的两个组件：<a href="http://download.java.net/javadesktop/swinglabs/releases/0.8/docs/api/org/jdesktop/swingx/JXPanel.html">JXPanel</a>和<a href="http://swinglabs.org/hudson/job/SwingX%20Weekly%20Build/javadoc/org/jdesktop/swingx/StackLayout.html">StackLayout</a>。JXPanel提供了一个方法(setAlpha)以方便地设置Panel的透明度(Alpha值)；而StackLayout允许在同一块区域内添加多层组件，并能同时展示所有层的组件(而，<a href="http://java.sun.com/javase/7/docs/api/java/awt/GridLayout.html">CardLayout</a>一次只能显示某一层的组件)。完整的示例程序如下所示，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;LoadDataDemo&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JFrame&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">5927602404779391420L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JXPanel&nbsp;dataPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;使用org.jdesktop.swingx.JXPanel，以方便设置清晰度</span><span style="color: #008000;"><br />
</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;LoadingPanel&nbsp;loadingPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;LoadDataDemo()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">LoadData&nbsp;Demo</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;initUI()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JButton&nbsp;button&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JButton(</span><span style="color: #000000;">"</span><span style="color: #000000;">Load&nbsp;Data</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;button.addActionListener(handler);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JXPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FlowLayout(FlowLayout.CENTER));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel.add(button);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadingPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;LoadingPanel(</span><span style="color: #000000;">"</span><span style="color: #000000;">Loading<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;使用org.jdesktop.swingx.StackLayout，将loadingPanel置于dataPanel的上方</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JPanel&nbsp;centerPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JXPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;StackLayout());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;centerPanel.add(dataPanel,&nbsp;StackLayout.TOP);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;centerPanel.add(loadingPanel,&nbsp;StackLayout.TOP);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Container&nbsp;container&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getContentPane();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.setLayout(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(centerPanel,&nbsp;BorderLayout.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">transient</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;ActionListener&nbsp;handler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ActionListener()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;将dataPanel及其子组件的清晰度设置为50%；并显示loadingPanel</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel.setAlpha(</span><span style="color: #000000;">0.5F</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadingPanel.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;thread&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Thread()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span style="color: #000000;">3000L</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;睡眠约3秒钟，以模拟加载数据的过程</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;数据加载完毕后，重新隐藏loadingPanel；并使dataPanel及其子组件重新清晰可见</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadingPanel.setVisible(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel.setAlpha(1F);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thread.start();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoadDataDemo&nbsp;demo&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;LoadDataDemo();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.setSize(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dimension(</span><span style="color: #000000;">400</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">300</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><strong><span style="font-size: 12pt;"><br />
3. 不使用SwingX</span></strong><br />
&nbsp;&nbsp;&nbsp; SwingX为我们提供了一系列功能强大，使用简易的Swing扩展组件，我强烈建议你去使用它。但若因故，你不准备使用它时，我们仍然有替代的解决方案，但此处仅简述一二。<br />
[1]对于设置Alpha值，需要创建一个继承自<a href="http://java.sun.com/javase/7/docs/api/javax/swing/JPanel.html">JPanel</a>的DataPanel类，覆写paintComponent方法，在其中使用Alpha合成，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,&nbsp;alpha));</span></div>
<span style="font-size: 10pt;">[2]对于StackLayout，我们可以使用GlassPane(玻璃窗格)或LayeredPane(分层窗格)进行替换，将LoadingPanel设置为GlassPane或LayeredPanel中的一层。由于一个JFrame只有一个GlassPane，为了程序的灵活性，一般首选使用LayeredPane。<br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/304120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2009-11-29 20:33 <a href="http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NO Sun Tech Days 2009(China)!!!(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/10/22/299328.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Thu, 22 Oct 2009 04:20:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/10/22/299328.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/299328.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/10/22/299328.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/299328.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/299328.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">NO Sun Tech Days 2009(China)!!!</span></span></strong><br />
</div>
<span style="font-size: 10pt;">今年的Sun科技日(中国站)将无法举办，<a href="http://developers.sun.com/events/techdays/locations.jsp">最新一届Sun科技日</a>将在2010年4月29-30日举行。(2009.10.23最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 在每年的下半年，都会举办Sun科技日(中国站)，但今年的Sun科技日却迟迟没有任何消息。由于Sun被Oracle收购的原故，一直担心以后再也不会有Sun科技日了(JavaOne正处于这样的猜测与争论之中)。幸运地是，Sun科技日还将举办下去；不幸地是，我今天上午刚刚确认了，今年中国站的Sun科技日将会缺失，最新的一次的Sun科技日将会在2010年4月29-30日举行。我猜想，这应该是由于Sun被收购造成了Sun科技日的不确定性，而当决定仍然举办时，已经无法在今年年内安排好会议了。从<a href="http://developers.sun.com/events/techdays/locations.jsp">最新日程表</a>来看，受影响的国家和地区不仅仅是中国，实际上2009年下半年只有巴西的圣保罗能举办Sun科技日。<br />
&nbsp;&nbsp;&nbsp; 一心等着今年的Sun科技日，看到这样的结果，只能说是悲喜参半。这其中掺杂着我对Sun科技日，Java，甚至是Sun Microsystems的一些私人情感，究其原因，无非我是一名Java开发者，特别是一位真心想学好Java，用好Java的开发者罢了。Sun Microsystems已经有了新的归宿，现在只希望Java的明天仍能美好，这也算是我的一点点私心吧。
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/299328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2009-10-22 12:20 <a href="http://www.blogjava.net/jiangshachina/archive/2009/10/22/299328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用SwingX与TimingFramework实现淡入淡出(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/09/28/296709.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 28 Sep 2009 01:46:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/09/28/296709.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/296709.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/09/28/296709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/296709.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/296709.html</trackback:ping><description><![CDATA[<div align="center"><strong style="font-size: 14pt;">利用SwingX与TimingFramework实现淡入淡出</strong><br />
</div>
<span style="font-size: 10pt;">本文使用<a href="https://swingx.dev.java.net">SwingX</a>与<a href="https://timingframework.dev.java.net">TimingFramework</a>展示了如何实现淡入淡出效果，程序简洁实用，希望对大家能有所助益。(2009.09.28最后更新)<br />
<br />
在Swing中使用<a href="http://java.sun.com/javase/6/docs/api/java/awt/AlphaComposite.html">AlphaComposite</a>设置界面的半透明度，再配合<a href="http://java.sun.com/javase/6/docs/api/javax/swing/Timer.html">javax.swing.Timer</a>可以比较容易地实现淡入淡出效果。但需要我们继承具体的UI组件，并重写它的paintComponent方法，同时还要为Timer提供一个ActionListener的实现。这些对于程序员来说，显得有些乏味。<br />
&nbsp;&nbsp;&nbsp; 幸运地是，SwingX中的JXPanel(JPanel的子类)通过setAlpha方法来设置半透明度，JXPanel中的所有UI组件都可随之半透明化。TimingFramework提供的Animator可以帮助我们非线性地设置JXPanel中的alpha值。故，我们在实现淡入淡出时，可以使用JXPanel来替代JPanel，用Animator来替代Timer。<br />
<img src="http://farm3.static.flickr.com/2567/3969155982_d8c1118b6c_o.png" alt="" /><br />
<img src="http://farm4.static.flickr.com/3452/3968381881_250129774e_o.png" alt="" /> <img src="http://farm3.static.flickr.com/2518/3969156126_0dbc8a627c_o.png" alt="" />
<br />
&nbsp;&nbsp;&nbsp; 下面是一个非常简单的示例，该示例在一个使用CardLayout的JPanel--cardPanel，中另包含了两个JXPanel--panelA和panelB，这两个子容器中又分别有一个JButton--buttonA和buttonB。当点击buttonA时，将淡入显示panelB；当点击buttonB时，将淡入显示panelA。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008080;">&nbsp;1</span>&nbsp;<span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;FadingCardDemo&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JFrame&nbsp;{<br />
</span><span style="color: #008080;">&nbsp;2</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">&nbsp;3</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">8005909309849021746L</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">&nbsp;4</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">&nbsp;5</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;CARD_A&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">CARD_A</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">&nbsp;6</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;CARD_B&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">CARD_B</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">&nbsp;7</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">&nbsp;8</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JPanel&nbsp;cardPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">&nbsp;9</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JXPanel&nbsp;panelA&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">10</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JXPanel&nbsp;panelB&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">11</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JButton&nbsp;buttonA&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">12</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JButton&nbsp;buttonB&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">13</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">14</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;FadingCardDemo()&nbsp;{<br />
</span><span style="color: #008080;">15</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">FadingButton&nbsp;Demo</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">16</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
</span><span style="color: #008080;">17</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />
</span><span style="color: #008080;">18</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">19</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">20</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;initUI()&nbsp;{<br />
</span><span style="color: #008080;">21</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cardPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;CardLayout());<br />
</span><span style="color: #008080;">22</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">23</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonA&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JButton(</span><span style="color: #000000;">"</span><span style="color: #000000;">Button&nbsp;A</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">24</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonA.addActionListener(actionHandler);<br />
</span><span style="color: #008080;">25</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;panelA&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JXPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
</span><span style="color: #008080;">26</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;panelA.add(buttonA,&nbsp;BorderLayout.CENTER);<br />
</span><span style="color: #008080;">27</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cardPanel.add(panelA,&nbsp;CARD_A);<br />
</span><span style="color: #008080;">28</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">29</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonB&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JButton(</span><span style="color: #000000;">"</span><span style="color: #000000;">Button&nbsp;B</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">30</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonB.addActionListener(actionHandler);<br />
</span><span style="color: #008080;">31</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;panelB&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JXPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
</span><span style="color: #008080;">32</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;panelB.add(buttonB,&nbsp;BorderLayout.CENTER);<br />
</span><span style="color: #008080;">33</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cardPanel.add(panelB,&nbsp;CARD_B);<br />
</span><span style="color: #008080;">34</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">35</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Container&nbsp;container&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getContentPane();<br />
</span><span style="color: #008080;">36</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.setLayout(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
</span><span style="color: #008080;">37</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(cardPanel,&nbsp;BorderLayout.CENTER);<br />
</span><span style="color: #008080;">38</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">39</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">40</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;ActionListener&nbsp;actionHandler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ActionListener()&nbsp;{<br />
</span><span style="color: #008080;">41</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">42</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Animator&nbsp;animator&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">43</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">44</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<br />
</span><span style="color: #008080;">45</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(animator&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
</span><span style="color: #008080;">46</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Animator(</span><span style="color: #000000;">2000</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">47</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator.setDeceleration(</span><span style="color: #000000;">0.2F</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">48</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator.setAcceleration(</span><span style="color: #000000;">0.4F</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">49</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"> if (animator.isRunning()) {<br />
</span><span style="color: #008080;">50</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator.stop();<br />
</span><span style="color: #008080;">51</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">52</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">53</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JButton&nbsp;button&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(JButton)&nbsp;e.getSource();<br />
</span><span style="color: #008080;">54</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(button&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;buttonA)&nbsp;{<br />
</span><span style="color: #008080;">55</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator.addTarget(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PropertySetter(panelB,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">alpha</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1.0F</span><span style="color: #000000;">));<br />
</span><span style="color: #008080;">56</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;panelB.setAlpha(</span><span style="color: #000000;">0.1F</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">57</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((CardLayout)&nbsp;cardPanel.getLayout()).show(cardPanel,&nbsp;CARD_B);<br />
</span><span style="color: #008080;">58</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(button&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;buttonB)&nbsp;{<br />
</span><span style="color: #008080;">59</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator.addTarget(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PropertySetter(panelA,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">alpha</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1.0F</span><span style="color: #000000;">));<br />
</span><span style="color: #008080;">60</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;panelA.setAlpha(</span><span style="color: #000000;">0.1F</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">61</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((CardLayout)&nbsp;cardPanel.getLayout()).show(cardPanel,&nbsp;CARD_A);<br />
</span><span style="color: #008080;">62</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">63</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;animator.start();<br />
</span><span style="color: #008080;">64</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">65</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;};<br />
</span><span style="color: #008080;">66</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">67</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />
</span><span style="color: #008080;">68</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SwingUtilities.invokeLater(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Runnable()&nbsp;{<br />
</span><span style="color: #008080;">69</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run()&nbsp;{<br />
</span><span style="color: #008080;">70</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FadingCardDemo&nbsp;demo&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FadingCardDemo();<br />
</span><span style="color: #008080;">71</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.setSize(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dimension(</span><span style="color: #000000;">400</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">300</span><span style="color: #000000;">));<br />
</span><span style="color: #008080;">72</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">73</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">74</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
</span><span style="color: #008080;">75</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">76</span>&nbsp;<span style="color: #000000;">}</span></div>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/296709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2009-09-28 09:46 <a href="http://www.blogjava.net/jiangshachina/archive/2009/09/28/296709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个简单的CheckBox Tree实现(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/08/05/289996.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Wed, 05 Aug 2009 13:10:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/08/05/289996.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/289996.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/08/05/289996.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/289996.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/289996.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">一个简单的CheckBox Tree实现</span></span></strong><br />
</div>
<span style="font-size: 10pt;">CheckBox Tree是一个十分常用的UI组件，它能使用户方便地按特定规则勾选树中的节点。</span><span style="font-size: 10pt;">本文实现了一种简单的Checking规则：当勾选了某节点后，该节点的所有下级节点全部被勾选；当取消勾选某节点后，该节点的所有下级节点全部被取消勾选。(2009.08.05最后更新)<br />
<br />
</span><span style="font-size: 10pt;">实现CheckBox Tree的常用方法，就是使用JCheckBox作为JTree的TreeCellRendrer，并且需要实现特定的Checking规则来勾选/取消勾选CheckBox。<br />
<br />
<strong><span style="font-size: 12pt;">1. 树节点</span></strong><br />
DefaultMutableTreeNode是最常用的TreeNode实现，此处我们将扩展这一实现--CheckBoxTreeNode，增加一个属性<span style="color: #2000ff;">isChecked</span>，用于标识该节点是否要被勾选上。该类的完整代码如下所示：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;CheckBoxTreeNode&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;DefaultMutableTreeNode&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">3195314943599939279L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isChecked&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;CheckBoxTreeNode(Object&nbsp;userObject)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(userObject);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isChecked()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;isChecked;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setChecked(</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isChecked)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.isChecked&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;isChecked;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
<strong><span style="font-size: 12pt;">2. 渲染器</span></strong><br />
如本文开头所述，我们将使用JCheckBox作为树节点展现形式的渲染器，同时确定对节点进行勾选或取消勾选的规则。CheckBoxTreeCellRenderer本身即是一个JCheckBox，那么在实现<span style="color: #2000ff;">getTreeCellRendererComponent</span>方法时，只简单地返回它自己的实例即可，而对于勾选或取消勾选的条件，则由CheckBoxTreeNode中的<span style="color: #2000ff;">isChecked</span>属性来确定，完整的代码如下所示：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;CheckBoxTreeCellRenderer&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JCheckBox&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;TreeCellRenderer&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">6432020851855339311L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;CheckBoxTreeCellRenderer()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setOpaque(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Component&nbsp;getTreeCellRendererComponent(JTree&nbsp;tree,&nbsp;Object&nbsp;value,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;selected,&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;expanded,&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;leaf,&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;row,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;hasFocus)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CheckBoxTreeNode&nbsp;node&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;((CheckBoxTreeNode)&nbsp;value);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;获取树节点对象。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setText(node.toString());&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;设置CheckBox所展示的文本。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;当树节点被设置为勾选时，则该节点对应的CheckBox被勾选上；否则，取消勾选。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(node.isChecked())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setSelected(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setForeground(Color.BLUE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setSelected(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setForeground(tree.getForeground());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
<strong><span style="font-size: 12pt;">3. 树</span></strong><br />
此处对JTree进行扩展，创建CheckBoxTree，该类只是为JTree添加了一个MouseListener，以侦听鼠标选中树节点后，如何设置勾选标记，并重绘树。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;CheckBoxTree&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JTree&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">217950037507321241L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;CheckBoxTree(TreeModel&nbsp;newModel)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(newModel);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addCheckingListener();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addCheckingListener()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseAdapter()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;mousePressed(MouseEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;row&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getRowForLocation(e.getX(),&nbsp;e.getY());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreePath&nbsp;treePath&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getPathForRow(row);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(treePath&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CheckBoxTreeNode&nbsp;node&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;((CheckBoxTreeNode)&nbsp;treePath.getLastPathComponent());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;checking&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">!</span><span style="color: #000000;">node.isChecked();&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;如果该节点已被勾选上，则此次将取消勾选；反之，亦反。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkNode(node,&nbsp;checking);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;repaint();&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;重绘整棵树。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;递归地勾选或取消勾选指定节点及其所有下级节点的CheckBox。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;checkNode(CheckBoxTreeNode&nbsp;node,&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;checking)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node.setChecked(checking);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">node.isLeaf())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Enumeration</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">CheckBoxTreeNode</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;children&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;node.children();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(children.hasMoreElements())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkNode(children.nextElement(),&nbsp;checking);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">上述程序有两个关键点：1. 设置当前节点及其子节点的勾选标记--<span style="color: #5000ff;">checkNode</span>；2. 重绘树--<span style="color: #2000ff;">repaint</span>。调用repaint方法对树进行绘制时，方法<span style="color: #2000ff;">TreeCellRenderer.getTreeCellRendererComponent</span>就会被调用，此时程序就会根据<span style="color: #2000ff;">checkNode</span>方法设定的<span style="color: #2000ff;">isChecked</span>来勾选或取消勾选对应的树节点CheckBox。简言之，就是先设置树型数据中的勾选标记，然后渲染器再根据这些标记来渲染CheckBox。<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/289996.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2009-08-05 21:10 <a href="http://www.blogjava.net/jiangshachina/archive/2009/08/05/289996.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>树的汇总(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/06/26/283467.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Thu, 25 Jun 2009 23:11:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/06/26/283467.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/283467.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/06/26/283467.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/283467.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/283467.html</trackback:ping><description><![CDATA[<div align="center">
<strong><font size="-1"><span style="font-size: 14pt;">树的汇总</span></font></strong><br />
</div>
<font size="-1">继上次浅谈了<a href="http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html">树的遍历</a>之后，这次再浅谈一下树的汇总。<wbr>此处的汇总是指将树中某个节点的数据按指定的规则汇集到它的父节点中。<wbr>例如，可以将树节点中的数值累加到它的父节点中。<wbr>仍如<a href="http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html">树的遍历</a>一文，我将使用两种简单的算法，递归与和迭代，<wbr>来实现这一功能。(2009.08.09最后更新)<br />
<br />
<strong><span style="font-size: 12pt;">1. 树节点</span></strong><br />
仍然沿用<a href="http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html">树的遍历</a>一文中的TreeNode/<wbr>GenericTreeNode，为便于阅读，<wbr>将GenericTreeNode中的若干关键属性展示如下，<br />
</font>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;T&nbsp;userObject&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;parent&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;children&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />
}</span></div>
<font size="-1"><br />
<strong><span style="font-size: 12pt;">2. 递归法</span></strong><br />
仍然先从最简单的递归法开始，<br />
</font>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;Double&nbsp;recursiveGatherValue(GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Double&nbsp;sumValue&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(node.isLeaf())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;node.getUserObject();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sumValue&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;node.getUserObject();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;children&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;node.getChildren();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;size&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;children.size();&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;size;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Double&nbsp;bufGatherValue&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;recursiveGatherValue(children.get(i));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sumValue&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;bufGatherValue;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;node.setUserObject(sumValue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;sumValue;<br />
}</span></div>
<font size="-1">递归法还是一如既往的简单易懂。与递归遍历树相比，<wbr>递归汇总树的程序基本上没大的变化，我就不赘述了...<br />
<br />
<strong><span style="font-size: 12pt;">3. 迭代法</span></strong><br />
与迭代遍历树相比，迭代汇总树的程序有一些明显的变化。<wbr>当初在思考迭代法时，有个问题一直困绕着我：<wbr>如何将下级节点的值赋给它的父节点，并且父节点的值要不断的进行累加。在<a href="http://www.javaworld.com.tw/jute/index.html">JavaWorld@TW</a>中提出这个问题之后<wbr>，很快就得到了<a href="http://www.javaworld.com.tw/jute/post/view?bid=29&amp;id=253828&amp;sty=1">清晰的解答</a>(真的很感谢社区里的大大们)。<wbr>毫无疑问，用迭代法遍历一棵树时需要使用一个栈，但同时，为了维护节点与汇总值之间的关系，<wbr>还需要另一个栈进行辅助。具体程序如下所示，<br />
</font>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;iterativeGatherValue(GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Stack</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">NodeValueTuple</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;nodeValueStack&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Stack</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">NodeValueTuple</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Stack</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;nodeStack&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Stack</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;nodeStack.push(node);<br />
&nbsp;&nbsp;&nbsp;&nbsp;Double&nbsp;sumValue&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Double(</span><span style="color: #000000;">0.0D</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">nodeStack.isEmpty())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;bufNode&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;nodeStack.pop();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(bufNode&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NodeValueTuple</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;bufNodeValueTuple&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;nodeValueStack.pop();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bufNodeValueTuple.node.setUserObject(sumValue);<br />
<br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sumValue&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;"> </span><span style="color: #000000;">bufNodeValueTuple.value</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(bufNode.isLeaf())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sumValue&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;bufNode.getUserObject();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nodeValueStack.add(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;NodeValueTuple</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Double</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">(bufNode,&nbsp;sumValue));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sumValue&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Double(</span><span style="color: #000000;">0.0D</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nodeStack.push(</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nodeStack.addAll(bufNode.getChildren());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<font size="-1">在遍历树的过程中，会将某节点N与它的汇总值一同置入一个栈(<wbr>nodeValueStack)中，当节点N有子节点时，<wbr>则将它的子节点及其汇总值也置入栈中，节点N与它的子节点之间使用一个NULL值进行分隔；<wbr>如果节点N是叶节点则累加汇总值；如果节点N为NULL，<wbr>则表示子节点们的汇总已结束。<br />
NodeValueTuple是一个二元组，<wbr>用于维护节点与它的汇总值之间的关系，代码如下所示，<br />
</font>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;NodeValueTuple</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">V</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">V</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;V&nbsp;value;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;NodeValueTuple(GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">V</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node,&nbsp;V&nbsp;value)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.node&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;node;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.value&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<font size="-1">在上述的汇总中，均只累加了叶节点中的数值，<wbr>而不管分枝节点和根节点本身所拥有的数值。<wbr>如果要累加这些节点本身的数值，应该如何做呢？<wbr>大家自己做做看吧，肯定非常简单 ^_^<br />
<br />
<strong><span style="font-size: 12pt;">4. 小结</span></strong><br />
树的汇总肯定是一个十分常见的应用，除了汇总数据之外，<wbr>我们还可以汇集节点中的对象，<wbr>如汇总挂载在节点上的集合对象中的元素，<wbr>使得父节点能够拥有所有子节点所拥有的元素。上述方法的效率应该不算低，<wbr>主要是因为所有的树节点只需要访问一次。<br />
<br />
</font><img src ="http://www.blogjava.net/jiangshachina/aggbug/283467.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2009-06-26 07:11 <a href="http://www.blogjava.net/jiangshachina/archive/2009/06/26/283467.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>树的遍历(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Wed, 01 Apr 2009 12:40:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/263241.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/263241.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/263241.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;">
<div align="center"><strong><span style="font-size: 14pt;"><strong>树的遍历</strong></span><br />
</strong></div>
之前的工作都没有接触到树，也就很少研究它。幸运地的是，在目前的工作中多次遇到树型结构的数据，那么访问树节点中的数据就是必然的了，而且还需要按照指定规则对节点中的数据进行额外处理。经过学习之后，对与树相关的基本算法有了一些认知，就计划写几篇小文。其实这样的文章早已是汗牛充栋，而我只是把它当作我的学习总结罢了，以加深记忆与理解，如能对其他朋友有所助益，则更感愉悦了 :-) (2009.04.03最后更新)<br />
<br />
这次先从最基础的开始--树的遍历。本文使用了两种极常用的方法来遍历树中的所有节点--递归；迭代，但它们实现的都是深度优先(Depth-First)算法。<br />
<br />
<strong style="font-size: 12pt;">1. 树节点与数据</strong><br />
先定义树节点及数据(用户对象)，并创建测试用的数据。<br />
TreeNode是树节点的定义。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;*&nbsp;树节点的定义。<br />
&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;获取指定下标处的子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;index<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下标。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;getChildAt(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;index);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;返回指定子节点的下标。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;index<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下标。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;getChildIndex(TreeNode&nbsp;index);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;获取子节点的数量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;子节点的数量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;getChildCount();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;返回父节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;父节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;getParent();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;设置父节点。注：此处不需要改变父节点中的子节点元素。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;parent<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;父节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setParent(TreeNode&nbsp;parent);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;获取所有的子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;子节点的集合。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;List</span><span style="color: #000000;">&lt;?&gt;</span><span style="color: #000000;">&nbsp;getChildren();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;是否为叶节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;是叶节点，返回true；否则，返回false。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isLeaf();<br />
}<br />
</span></div>
<span style="font-size: 10pt;"><br />
GenericTreeNode是一个通用的树节点实现。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;T&nbsp;userObject&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;parent&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;children&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;GenericTreeNode(T&nbsp;userObject)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.userObject&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;userObject;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;GenericTreeNode()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">(</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;添加子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;child<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addChild(GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;child)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;children.add(child);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.setParent(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;删除指定的子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;child<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;removeChild(TreeNode&nbsp;child)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removeChildAt(getChildIndex(child));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;删除指定下标处的子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;index<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下标。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;removeChildAt(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;index)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeNode&nbsp;child&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getChildAt(index);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;children.remove(index);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.setParent(</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;getChildAt(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;index)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;children.get(index);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;getChildCount()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;children.size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;getChildIndex(TreeNode&nbsp;child)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;children.indexOf(child);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;getChildren()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;Collections.unmodifiableList(children);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setParent(TreeNode&nbsp;parent)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.parent&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;parent;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TreeNode&nbsp;getParent()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;parent;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;是否为根节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;是根节点，返回true；否则，返回false。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isRoot()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;getParent()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isLeaf()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;getChildCount()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;判断指定的节点是否为当前节点的子节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;node<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;是当前节点的子节点，返回true；否则，返回false。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isChild(TreeNode&nbsp;node)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(node&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(getChildCount()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(node.getParent()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;T&nbsp;getUserObject()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;userObject;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setUserObject(T&nbsp;userObject)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.userObject&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;userObject;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;toString()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;userObject&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">&nbsp;:&nbsp;userObject.toString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span></div>
<br />
<span style="font-size: 10pt;">UserObject是节点上的用户对象，相当于是数据。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;UserObject&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;name&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Integer&nbsp;value&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Integer.valueOf(</span><span style="color: #000000;">0</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;UserObject()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;UserObject(String&nbsp;code,&nbsp;Integer&nbsp;value)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.name&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;code;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.value&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;getName()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setName(String&nbsp;code)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.name&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;code;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Integer&nbsp;getValue()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setValue(Integer&nbsp;value)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.value&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;toString()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;StringBuilder();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.append(</span><span style="color: #000000;">"</span><span style="color: #000000;">[name=</span><span style="color: #000000;">"</span><span style="color: #000000;">).append(name).append(</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">).append(value).append(</span><span style="color: #000000;">"</span><span style="color: #000000;">]</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result.toString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span></div>
<br />
<span style="font-size: 10pt;">TreeUtils是用于创建树的工具类。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TreeUtils&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;buildTree()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;root&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;root.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">ROOT</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">0</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node1&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node1.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">1</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">0</span><span style="color: #000000;">)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node2&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node2.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">2</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">0</span><span style="color: #000000;">)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node3&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node3.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">3</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">5</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;root.addChild(node1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;root.addChild(node2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;root.addChild(node3);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node11&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node11.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">11</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">0</span><span style="color: #000000;">)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node21&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node21.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">21</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">0</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node1.addChild(node11);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node2.addChild(node21);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node111&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node111.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">111</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">3</span><span style="color: #000000;">)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node112&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node112.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">112</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">9</span><span style="color: #000000;">)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node211&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node211.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">211</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">6</span><span style="color: #000000;">)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node212&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node212.setUserObject(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;UserObject(</span><span style="color: #000000;">"</span><span style="color: #000000;">212</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;Integer.valueOf(</span><span style="color: #000000;">3</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node11.addChild(node111);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node11.addChild(node112);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node21.addChild(node211);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node21.addChild(node212);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;root;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span></div>
<span style="font-size: 10pt;"><br />
<strong style="font-size: 12pt;">2. 递归法</strong><br />
使用递归法的最大好处就是--简单，但一般地，我们都认为递归的效率不高。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;recursiveTravel(GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;travelNode(node);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;访问节点，仅仅只是打印该节点罢了。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;children&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;node.getChildren();<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;children.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recursiveTravel(children.get(i));&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;递归地访问当前节点的所有子节点。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">大家肯定知道，系统在执行递归方法(对于其它方法也是如此)时是使用运行时栈。对方法的每一次调用，在栈中都会创建一份此次调用的活动记录--包括方法的参数，局部变量，返回地址，动态链接库，返回值等。<br />
既然系统能够隐式地使用栈去执行递归方法，那么我们就可以显式地使用栈来执行上述递归程序，这也是将递归程序转化为迭代程序的常用思想。下面的iterativeTravel方法就运用了这一思想。<br />
<br />
<strong style="font-size: 12pt;">3. 迭代法</strong><br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;iterativeTravel(GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;node)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Stack</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;nodes&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Stack</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;nodes.push(node);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;将当前节点压入栈中。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">nodes.isEmpty())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenericTreeNode</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">UserObject</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;bufNode&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;nodes.pop();&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;从栈中取出一个节点。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;travelNode(bufNode);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;访问节点。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">bufNode.isLeaf())&nbsp;{&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;如果该节点为分枝节点，则将它的子节点全部加入栈中。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nodes.addAll(bufNode.getChildren());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">与递归法相比，迭代法的代码略多了几行，但仍然很简单。<br />
<strong><br />
<span style="font-size: 12pt;"><strong>4. 小结</strong><br />
</span></strong>由于上述两种方法均(隐式或显式地)使用了运行栈，所以此处的迭代法并不能提高整个程序的效率。相反地，由于在应用程序中显式地使用栈(java.util.Stack)，iterativeTravel方法的效率可能反而更低。但iterativeTravel的最大好处是，能够有效地避免运行时栈溢出(java.lang.StackOverflowError)。<br />
如果树的层次不太深，每层的子节点数不太多，那么使用递归法应该是没有问题的。毕竟，简洁地程序会提供更多的好处。<br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/263241.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2009-04-01 20:40 <a href="http://www.blogjava.net/jiangshachina/archive/2009/04/01/263241.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sun Tech Days 2008 -- Photos(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/11/23/242122.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 23 Nov 2008 14:37:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/11/23/242122.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/242122.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/11/23/242122.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/242122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/242122.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 14pt;"><strong>Sun Tech Days 2008 -- Photos</strong></span><br />
</strong></div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 今年Sun科技日会场的一些照片，大家可以浏览一下。(2008.11.23最后更新)<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 虽然报名参加了今年的Sun科技日，但由于工作的原因，实际上没有亲临现场，所以这次就不能把自己的所见，所闻，所思，所感呈现给大家了:-(<br />
&nbsp;&nbsp;&nbsp; 当得知周六也无法参会时，就让一位朋友拿着我的门票去参加了最后一天的会程，拍了一些照片，算是弥补了一点儿小小的遗憾吧。大家有兴趣的话，可以看看...噢，请关注可爱的小Duke(这是我亲手拍的*_*</span>)<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3160/3049660317_4a2822e107.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3150/3049660347_4305f86246.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3073/3049660285_a27561d653.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3247/3049660271_f8bfe0f0bb.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3247/3049660235_b8195c9772.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3247/3049660209_d8cac8e88d.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3008/3051696767_e025713b1a.jpg?v=0" /><br />
<br />
<img alt="STD 2008" src="http://farm4.static.flickr.com/3276/3051696773_7f5bd0c815.jpg?v=0" /><br />
<br />
<img alt="STD 2008 - Duke" src="http://farm4.static.flickr.com/3022/3051775669_6351f096fd.jpg?v=0" /><br />
<br />
<img alt="STD 2008 - Duke" src="http://farm4.static.flickr.com/3152/3051775683_de998c9a6d.jpg?v=0" /><br />
<br />
<span style="font-size: 12pt;"><strong>&nbsp;&nbsp;&nbsp; 也请关注我的系列博文--</strong><a href="http://www.blogjava.net/jiangshachina/category/27188.html"><strong>JavaOne/Sun Tech Days</strong></a><strong>。</strong></span><img src ="http://www.blogjava.net/jiangshachina/aggbug/242122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2008-11-23 22:37 <a href="http://www.blogjava.net/jiangshachina/archive/2008/11/23/242122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sun Tech Days 2008 -- Preview(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/11/16/240784.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 16 Nov 2008 03:06:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/11/16/240784.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/240784.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/11/16/240784.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/240784.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/240784.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong style="font-size: 14pt;">Sun Tech Days 2008 -- Preview</strong></span><br />
</div>
<span style="font-size: 10pt;"><strong style="font-size: 14pt;">
</strong>&nbsp;&nbsp;&nbsp; 一年一度的<a href="http://www.suntechdays.cn/">Sun Tech Days</a>(STD)又将开幕了，本文简介了本届STD中我所期待的若干主题，有兴趣的朋友可以浏览一下。(2008.11.16最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 虽然已经报名了(前几天刚拿到门票)，但今年最终是否能够参会，还要看是否有时间(刚换了新工作，任务比较重，还要学习很多新知识)。如果能够如愿参加，那么这将是我连续第四年参加STD了(也很不容易啊^_^)，关于以前的参会情况，大家可以看看我的系列博文<a href="../../jiangshachina/category/27188.html">Sun Tech Days</a>。<br />
&nbsp;&nbsp;&nbsp; 世界在不断地变化，...，说到这里，先插几句题外话。在刚开始动笔写这篇文章的时候，从<a href="http://www.theserverside.com/">TSS</a>上看到<a href="http://www.theserverside.com/news/thread.tss?thread_id=51863">Sun裁员6000人</a>的消息，这也算是一种变化吧。不过也没什么特别的，很正常的商业行为(特别是在当前经济气候下)。言归正传，Java在过去的一年中也有了新的进展，但我学识浅薄，不可能对其进行深入地分析与总结，不过对于自己感兴趣的几个方面还是有些了解的，每年STD的主题自然也与这些新技术或新进展密不可分。<br />
&nbsp;&nbsp;&nbsp; <strong>Java SE 6 Update 10</strong><br />
&nbsp;&nbsp;&nbsp; 在经过了"漫长"地等待之后，就在前不久，<a href="http://java.sun.com/javase/downloads/?intcmp=1281">Java SE 6 Update 10</a>(曾名Consumer JRE，Java SE 6 Update N)终于正式发布了。对于新鲜出炉的Update 10，STD自然不会放过，今年就有一个名为"Java SE 6 Update 10和配置"的技术session。我一直在关注Update 10，也在关注桌面应用，希望这次能好好听听。之前也写有两篇相关的文章--<a href="../../jiangshachina/archive/2008/10/17/234849.html">Java SE 6 Update 10发布了</a>和<a href="../../jiangshachina/archive/2008/07/14/214703.html">下一代Java Applet插件技术</a>，大家也可以看看。<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>JavaFX</strong><br />
&nbsp;&nbsp;&nbsp; 由于十分喜欢Java桌面开发，所以对JavaFX也有几分兴趣，但一直没有体验过。JavaFX的发展似乎并不如人意，较为缓慢，与其它的主流RIA技术相比，真不知道它最终会有多少市场份额。这次会有一个"<em>RIA使用JavaFX</em>"的主题，正好该时段也没有其它中意的session，那就听听它好了。<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>MySQL</strong><br />
&nbsp;&nbsp;&nbsp; 数据库永远都是我最关注的领域之一，而且<a href="http://www.mysql.com/">MySQL</a>正是我最喜欢、最擅长的数据库。Sun在今年年初收购了MySQL(还好年初就收购了，如果到了下半年，Sun肯定就没那么多钱了，世界真是瞬息万变啊)，虽然这与Java没有直接的关系，但仍然值得Java人兴奋一下。"<em>MySQL: Web 2.0数据库</em>"，这个主题应该是吸引人的。另，今年没有<a href="http://developers.sun.com/javadb/">Java DB</a>的主题，略感遗憾。不过，如果仍然像前两年那样，只是简介一下Java DB，而且内容基本相同，那确实也没必要再要这个主题了。<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>Comet</strong><br />
&nbsp;&nbsp;&nbsp; 之前对Comet知之甚少，但经常看到这个名词。有人说它是下一代Ajax技术...，但我是不懂了，那就去听听吧。<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>Java性能</strong><br />
&nbsp;&nbsp;&nbsp; 程序的性能总是大家最关心的问题之一，我也不例外。希望这次能在"<em>提高Java性能</em>"这个session中学习到一些窍门 *_*<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/240784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2008-11-16 11:06 <a href="http://www.blogjava.net/jiangshachina/archive/2008/11/16/240784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java SE 6 Update 10发布了!(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/17/234849.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Fri, 17 Oct 2008 13:37:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/17/234849.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/234849.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/17/234849.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/234849.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/234849.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong style="font-size: 14pt;">Java SE 6 Update 10发布了!</strong></span><br />
</div>
<span style="font-size: 10pt;">
&nbsp;&nbsp;&nbsp; 在经过了漫长的期待之后，<a href="http://java.sun.com/javase/downloads/index.jsp">Java SE 6 Update 10</a>终于正式发布了。本文主要简介了该版本在客户端应用方面的改进。(2008.10.18最后更新)<br />
<br />
Java SE 6本就加入了许多关于客户端应用的新特性，在<a href="http://blogs.sun.com/dannycoward/entry/java_se_6_top_ten">Java SE 6的十大新特性</a>中，直接关于客户端/桌面领域的新特性就有两处。此次Update 10更是集中地在该领域内做出了重大改进。此处，主要基于<a href="http://java.sun.com/javase/6/webnotes/6u10.html">Java SE 6 Update 10 Release Notes</a>的说明，简介了该版本在客户端应用方面的改进。<br />
<br />
<strong style="font-size: 12pt;">下一代Java Plug-in</strong><br />
&nbsp;&nbsp;&nbsp; 下一代Java插件包含了Applet和Java Web Start技术的最佳架构特性。它为在Web浏览器中发布Java和Java FX内容提供了健壮的平台。下一代Java插件为高级用户内容和企业级应用提供了许多强大的特性。<br />
&nbsp;&nbsp;&nbsp; * 允许为Applet申请更多的内存；可为每个Applet应用指定不同的命令行参数。<br />
&nbsp;&nbsp;&nbsp; * 为每个Applet应用选择不同的JRE。<br />
&nbsp;&nbsp;&nbsp; * 提高了可靠性。<br />
&nbsp;&nbsp;&nbsp; * 能在Java和JavaScript程序设计语言之间进行更好、更便捷的整合。<br />
&nbsp;&nbsp;&nbsp; * 增强了对访问Web页面中DOM的支持。<br />
&nbsp;&nbsp;&nbsp; * 增加了对Web Services的支持。<br />
内建支持JNLP，使得Applet应用可立即复用之前由Java Web Start应用使用的JNLP扩展，例如JavaFX运行时组件，对基于硬件加速的3D图形，视频回放等等。<br />
&nbsp;&nbsp;&nbsp; P.S. 在我较早前的一篇译文<strong><a href="http://www.blogjava.net/jiangshachina/archive/2008/07/14/214703.html">下一代Java Applet插件技术</a></strong>中，对这个新的插件有较详尽的描述。<br />
<br />
<strong style="font-size: 12pt;">Java Quick Starter</strong><br />
&nbsp;&nbsp;&nbsp; Java Quick Starter(JQS)为了提高Applet程序和Java应用程序的初始启动速度，会周期性预加载一些常用的JRE文件到内存(占用不多于20M的内存)。之后，当Java程序启动时，磁盘I/O将会显著减少，那么启动速度也就大大加快了。JQS会作为一个Windows服务启动，在任务管理器中会看到一个名为jqs.exe的进程。<br />
&nbsp;&nbsp;&nbsp; 注：JQS仅能用于x86架构下的Windows 2000和Windows XP系统；由于Windows Vista系统有其自己的预加载机制，JQS将不能使用。<br />
<br />
<strong style="font-size: 12pt;">Deployment Toolkit</strong><br />
&nbsp;&nbsp;&nbsp; 为了避免浏览器的兼容性问题，Deployment Toolkit提供了一组JavaScript方法，能够自动生成部署Applet和Java Web Start程序时所使用的HTML语句。<br />
<br />
<strong style="font-size: 12pt;">Nimbus</strong><br />
&nbsp;&nbsp;&nbsp; Nimbus是一个新的Java跨平台外观。在JDK 6 Update 10自带的demo中新加了一个SwingSet3应用，但与其它的demo应用(如SwingSet2)不同，这个demo应用没有包含源代码和可执行的jar文件，而需要通过Java Web Start运行(我猜想SwingSet3应该还处于开发阶段)。官方的介绍中有一张图片，大家可以看看，确实漂亮多了(此前，默认的Metal外观已经多年未更新了)。<br />
<img alt="" src="http://java.sun.com/javase/6/docs/technotes/guides/jweb/images/NimbusLAF.png" />&nbsp;<br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/234849.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2008-10-17 21:37 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/17/234849.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JStockChart -- Getting Started(Timeseries)(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/09/08/227555.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 08 Sep 2008 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/09/08/227555.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/227555.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/09/08/227555.html#Feedback</comments><slash:comments>15</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/227555.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/227555.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 14pt;">JStockChart -- Getting Started(Timeseries)</span></strong><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp; 本文以一个示例介绍了如何利用</span><span style="font-size: 10pt;"><a href="http://code.google.com/p/jstockchart/">JStockChart</a>生成金融时序图。(2009.11.23最后更新)<br />
<br />
<strong style="font-size: 12pt;">1. 环境要求</strong><br />
&nbsp;&nbsp;&nbsp; 需要JDK 1.5或更高版本。<br />
<br />
<strong style="font-size: 12pt;">2. 下载与安装<br />
</strong>&nbsp;&nbsp;&nbsp; 从<a href="http://code.google.com/p/jstockchart">JStockChart</a>的站点中<a href="http://code.google.com/p/jstockchart/downloads/list">下载</a>它的最新版本，当前版本是0.4.2。<br />
&nbsp;&nbsp;&nbsp; 解压JStockChart的zip文件(jstockchart-0.4.2.zip)后，将jstockchart目录中的jstockchart-0.4.2.jar和jstockchart/lib目录中的jcommon-1.0.16.jar和jfreechart-1.0.13.jar加到classpath中。<br />
<br />
<span style="font-size: 12pt;"><strong>3. 示例</strong></span><br />
&nbsp;&nbsp;&nbsp; 在JStockChart的发行包中，有一个demo目录。该目录包含了一个简单的例子，展示了如何利用JStockChart去生成时序图。demo目录中有一个Ant脚本build.xml，在该目录下执行命令ant即可运行该示例程序。下面是该示例的关键代码及其说明。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TimeseriesChartDemo&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IOException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;<img alt="" src="http://www.blogjava.net/Images/dot.gif" /><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建一个包含TimeseriesItem的List实例。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 在此处，通过查询<a href="http://www.db4o.com">db4o</a>的数据库文件，直接获得该List．<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;注意：所有的数据，都必须在"同一天"内。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;data&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;<img alt="" src="http://www.blogjava.net/Images/dot.gif" />;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建SegmentedTimeline实例，表示时间区间"00:00-11:30，13:00-24:00"(即24小时中空缺了"11:31-12:59"这段时间)。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SegmentedTimeline&nbsp;timeline&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SegmentedTimeline(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SegmentedTimeline.MINUTE_SEGMENT_SIZE,&nbsp;</span><span style="color: #000000;">1351</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">89</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeline.setStartTime(SegmentedTimeline.firstMondayAfter1900()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">780</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;SegmentedTimeline.MINUTE_SEGMENT_SIZE);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建TimeseriesDataset实例，时间间隔为1分钟。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeseriesDataset&nbsp;dataset&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TimeseriesDataset(Minute.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">,&nbsp;timeline,&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;向dataset中加入TimeseriesItem的List。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataset.addDataItems(data);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建逻辑价格坐标轴。指定中间价为21，显示9个坐标值，坐标值的格式为".00"。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CentralValueAxis&nbsp;fixedPriceAxis&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;CentralValueAxis(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Double(</span><span style="color: #000000;">"</span><span style="color: #000000;">21</span><span style="color: #000000;">"</span><span style="color: #000000;">),&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Range(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataset.getMinPrice().doubleValue(),&nbsp;dataset.getMaxPrice().doubleValue()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">9</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;DecimalFormat(</span><span style="color: #000000;">"</span><span style="color: #000000;">.00</span><span style="color: #000000;">"</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建价格区域</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PriceArea&nbsp;priceArea&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PriceArea(fixedPriceAxis);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建逻辑量坐标轴。显示5个坐标值，坐标值的格式为"0"。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FixedNumberAxis&nbsp;fixedVolumeAxis&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FixedNumberAxis(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Range(dataset<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.getMinVolume().doubleValue(),&nbsp;dataset.getMaxVolume()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.doubleValue()),&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;DecimalFormat(</span><span style="color: #000000;">"</span><span style="color: #000000;">0</span><span style="color: #000000;">"</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建量区域</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VolumeArea&nbsp;volumeArea&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;VolumeArea(fixedVolumeAxis);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建时序图区域</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeseriesArea&nbsp;timeseriesArea&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TimeseriesArea(priceArea,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;volumeArea,&nbsp;createFixedDateAxis(DateUtils.createDate(</span><span style="color: #000000;">2008</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;通过JStockChartFactory的工厂方法生成JFreeChart实例。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;指定了该图的标题为"Timeseries&nbsp;Chart&nbsp;Demo"，并且不生成图例(legend)。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JFreeChart&nbsp;jfreechart&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;JStockChartFactory.createTimeseriesChart(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Timeseries&nbsp;Chart&nbsp;Demo</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;dataset,&nbsp;timeline,&nbsp;timeseriesArea,&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img alt="" src="http://www.blogjava.net/Images/dot.gif" /><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;创建图像文件。图像格式为PNG，长为545，宽为300。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChartUtilities.saveChartAsPNG(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;File(imageFile),&nbsp;jfreechart,&nbsp;</span><span style="color: #000000;">545</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">300</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;指定时期坐标轴中的逻辑坐标。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;FixedDateAxis&nbsp;createFixedDateAxis(Date&nbsp;baseDate)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FixedDateAxis&nbsp;fixedDateAxis&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FixedDateAxis(baseDate,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SimpleDateFormat(</span><span style="color: #000000;">"</span><span style="color: #000000;">HH:mm</span><span style="color: #000000;">"</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">09:30</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;TickAlignment.START);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">10:00</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">10:30</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">11:00</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">11:30</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;TickAlignment.END);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">13:00</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;TickAlignment.START);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">13:30</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">14:00</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">14:30</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fixedDateAxis.addDateTick(</span><span style="color: #000000;">"</span><span style="color: #000000;">15:00</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;TickAlignment.END);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;fixedDateAxis;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">运行该示例程序后，可以生成如下图片：<br />
<img alt="jstockchart-timeseries" src="http://farm4.static.flickr.com/3168/2834979157_8d4d2d05da_o.png" height="300" width="545" /><br />
<br />
<strong style="font-size: 12pt;">4. 定制图表<br />
</strong>&nbsp;&nbsp;&nbsp; 由于JFreeChart具有高度的可定制性，所以当生成了JFreeChart实例后，你可以从中获取图表中的各个元素，如Plot，然后再定制它们的属性。详细信息请参见JFreeChart的站点和论坛。<br />
&nbsp;&nbsp;&nbsp; 另外，在创建JStockChart的XXXArea对象时，也可以直接定制若干属性。如，通过PriceArea类，可以设置价格线的颜色(PriceArea.setPriceColor)，均线的颜色(PriceArea.setAverageColor)；是否显示均线(PriceArea.setAverageVisible)；是否显示涨跌幅(PriceArea.setRateVisible)；...。详细信息请参见JStockChart的API文档。<br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/227555.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2008-09-08 16:12 <a href="http://www.blogjava.net/jiangshachina/archive/2008/09/08/227555.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>