﻿<?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-随笔分类-Java</title><link>http://www.blogjava.net/jiangshachina/category/23789.html</link><description>a cup of Java, cheers!</description><language>zh-cn</language><lastBuildDate>Sun, 09 Oct 2016 07:14:59 GMT</lastBuildDate><pubDate>Sun, 09 Oct 2016 07:14:59 GMT</pubDate><ttl>60</ttl><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>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 SE 8流处理数据II(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/08/15/417011.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Fri, 15 Aug 2014 11:57:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/08/15/417011.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/417011.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/08/15/417011.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/417011.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/417011.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">利用Java SE 8流处理数据</strong><br style="font-size: 14pt;" />-- 结合Stream API的高级操作去表示富数据处理查询</div><br /><span style="font-size: 10pt;">本文是</span><a href="http://www.oracle.com/technetwork/java/javamagazine/index.html"><span style="font-size: 10pt;">Java Magazine</span></a><span style="font-size: 10pt;"> 201405/06刊中的一篇文章，也是文章系列"利用Java SE 8流处理数据"中的第二篇，它基于flatMap()和collect()介绍了Java流的高级用法(2014.08.15最后更新)</span><br /><br /><span style="font-size: 10pt;">在本系列的第一篇文章中，你看到了Java流让你能够使用与数据库操作相似的方法去处理集合。作为一个复习，清单1的例子展示了如何使用Stream API去求得大交易的金额之和。我们组建了一个管道，它由中间操作(filter和map)与最终操作(reduce)构成，图1形象地展示它。</span><br /><span style="font-size: 10pt;">清单1</span><br /><span style="font-size: 10pt;">int sumExpensive = </span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transactions.stream()</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .filter(t -&gt; t.getValue() &gt; 1000)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .map(Transaction::getValue)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .reduce(0, Integer::sum);</span><br /><em><strong><span style="font-size: 10pt;">图1</span></strong></em><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140506_stream_01.png" height="135" width="517" /><br /><span style="font-size: 10pt;">然而在系列的第一部分中，并没有研究这两个方法：</span><br /><span style="font-size: 10pt;">flatMap：这是一个中间操作，它允许将一个"map"和一个"flatten"操作结合在一起</span><br /><span style="font-size: 10pt;">collect：这是一个最终操作，它依据不同的方式，将流中的元素归集为一个结果。</span><br /><span style="font-size: 10pt;">这两个方法对于表达更为复杂的查询是十分有用的。例如，你可以将flatMap和collect结合起来，生成代表一个文字流中每个字母出现的次数的Map对象，如清单2所示。如果第一次看到这段代码觉得很惊奇时，但请不要担心。本文的目的就是要解释并探究这两个方法更多的细节。</span><br /><em><strong><span style="font-size: 10pt;">清单2</span></strong></em><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;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;java.util.function.Function.identity;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;java.util.stream.Collectors.</span><span style="color: #000000; ">*</span><span style="color: #000000; ">;<br /><br />Stream</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;words&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Stream.of(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Java</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Magazine</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">is</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">the</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">best</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Long</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;letterToCount&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;words.map(w&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;w.split(</span><span style="color: #000000; ">""</span><span style="color: #000000; ">))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.flatMap(Arrays::stream)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(groupingBy(identity(),&nbsp;counting()));</span></div><span style="font-size: 10pt;">清单2中的代码将会生成如清单3示的结果。棒极了，不是吗？让我们开始探究flatMap和collect方法是如何工作的。</span><br /><em><strong><span style="font-size: 10pt;">清单3</span></strong></em><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; ">[a:</span><span style="color: #000000; ">4</span><span style="color: #000000; ">,&nbsp;b:</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;e:</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;g:</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;h:</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;i:</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;..]</span></div><br /><strong><span style="font-size: 12pt;">flatMap方法</span></strong><br /><span style="font-size: 10pt;">假设你想找出文件中所有独一唯二的字。你会怎么做呢？</span><br /><span style="font-size: 10pt;">你可能认为这很简单；我们可以Files.lines()，在前面的文章中已见过了这个方法，因为它会返回一个包含文件中所有行的流。然后我们就可以使用map方法将每一行拆分成字，最后再使用distinct方法去除重复的字。第一次尝试得到的代码可能如清单4所示。</span><br /><em><strong><span style="font-size: 10pt;">清单4</span></strong></em><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; ">Files.lines(Paths.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">stuff.txt</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(line&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;line.split(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">\\s+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;String[]&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.distinct()&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;String[]&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.forEach(System.out::println);</span></div><span style="font-size: 10pt;">不幸的是，这段程序并不十分正确。如果运行它，会得到令人生疑的结果，与下面的输出有些类似：</span><br /><span style="font-size: 10pt;">[Ljava.lang.String;@7cca494b</span><br /><span style="font-size: 10pt;">[Ljava.lang.String;@7ba4f24f</span><br /><span style="font-size: 10pt;">...</span><br /><span style="font-size: 10pt;">我们的第一次尝试确实打印出了代表几个流对象的字符串。那发生了什么呢？该方法的问题是，传给map方法的Lambda表达式返回的是文件中每一行的String数组(String[])。而我们真正想要的是一个表示文字的流的Stream&lt;String&gt;对象。</span><br /><span style="font-size: 10pt;">幸运的是，对于该问题有一个解决方案，就是使用flatMap方法。让我们一步一步地看看如何得到正确的解决方法。</span><br /><span style="font-size: 10pt;">首先，我们需要字的流，而不是数组的流。有一个名为Arrays.stream()的方法，它将使用一个数组作为参数，并生成一个流。请看清单5中的例子。</span><br /><em><strong><span style="font-size: 10pt;">清单5</span></strong></em><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; ">String[]&nbsp;arrayOfWords&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: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Magazine</span><span style="color: #000000; ">"</span><span style="color: #000000; ">};<br />Stream</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;streamOfwords&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.stream(arrayOfWords);</span></div><span style="font-size: 10pt;">让我们在前面的流管道中使用该方法，看看会发生什么(见清单6)。这个方案依然行不通。那是因为我们最终得到的是一组流的流(准确地说，就是Stream&lt;Stream&lt;String&gt;&gt;)。确切地是，我们首先将每一行转换为一个字的数组，然后使用方法Arrays.stream()将每一个数组转换成一个流。</span><br /><em><strong><span style="font-size: 10pt;">清单6</span></strong></em><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; ">Files.lines(Paths.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">stuff.txt</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(line&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;line.split(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">\\s+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;String[]&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Arrays::stream)&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;Stream&lt;String&gt;&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.distinct()&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;Stream&lt;String&gt;&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.forEach(System.out::println);</span></div><span style="font-size: 10pt;">我们使用flatMap()方法去解决这个问题，如清单7所示。使用flatMap()方法能够用流中的内容，而不是流去替换每一个生成的数组。换言之，通过map(Arrays::stream)方法生成的全部独立的流被合并或"扁平化"为一个流。图2形象地展示了使用flatMap()方法的效果。</span><br /><em><strong><span style="font-size: 10pt;">清单7</span></strong></em><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; ">Files.lines(Paths.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">stuff.txt</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(line&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;line.split(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">\\s+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">))&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;String[]&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.flatMap(Arrays::stream)&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;String&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.distinct()&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Stream&lt;String&gt;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.forEach(System.out::println);</span></div><span style="font-size: 10pt;">本质上，flatMap让你可以使用其它流去替换另一个流中的每个元素，然后再将所有生成的流连合并为一个流。</span><br /><span style="font-size: 10pt;">请注意，flatMap()是一个通用的模式，在使用Optaional或CompletableFuture时，你还会看到它。</span><br /><br /><strong><span style="font-size: 12pt;">collect方法</span></strong><br /><span style="font-size: 10pt;">现在让我们看看collect方法的更多细节。在本系列的第一篇文章中你所看到的方法，要么返回另一个流(即，这些方法是中间操作)，要么返回一个值，例如一个boolean，一个int，或一个Optional对象(即，这些方法是最终操作)。</span><br /><span style="font-size: 10pt;">collect就是一个最终方法，但它有点儿不同，因为你可以用它将一个Stream对象转为一个List对象。例如，为了得到一个包含有所有高金额交易ID的列表，你可以使用像清单8那样的代码。</span><br /><em><strong><span style="font-size: 10pt;">清单8</span></strong></em><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;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;java.util.stream.Collectors.</span><span style="color: #000000; ">*</span><span style="color: #000000; ">;<br /><br />List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;expensiveTransactionsIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getValue()&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><span style="font-size: 10pt;">传递给collect方法的参数就是一个类型为java.util.stream.Collector的对象。这个Collector对象是干什么的？本质上看，它描述了如何按照需要去收集流中的元素，再将它们生成为一个最终结果。之前用到的工厂方法Collector.toList()会返回一个Collector对象，它描述了如何将一个Stream对象归集为一个List对象。而且，Collctors内建有有许多相似的方法。例如，使用toSet()方法可以将一个Stream对象转化为一个Set对象，它会删除所有重复的元素。清单9中的代码展示了如何生成一个仅仅包含高金额交易所在城市的Set对象。(注意：在后面的例子中，我们假设Collectors类中的工厂方法都已通过语句import static java.util.stream.Collectors.*被静态引入了)</span><br /><strong><em><span style="font-size: 10pt;">清单9</span></em></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; ">Set</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;cities&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getValue()&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getCity)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toSet());</span></div><span style="font-size: 10pt;">注意，无法保证会返回何种类型的Set对象。但是，通过使用toCollection()，你可以进行更多的控制。例如，若你想得到一个HashSet，可以传一个构造器给toCollection方法(见清单10)。</span><br /><em><strong><span style="font-size: 10pt;">清单10</span></strong></em><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; ">Set</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;cities&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getValue()&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getCity)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toCollection(HashSet::</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">));</span></div><span style="font-size: 10pt;">然而，这并不是你能用collect和Collector所做的全部事情。实际上，这只是你能用它们所做的事情中的极小部分。下面是一些你所能表达的查询的例子：</span><br /><span style="font-size: 10pt;">将交易按货币分类，并计算每种货币的交易金额之和(返回一个Map&lt;Currency, Integer&gt;对象)</span><br /><span style="font-size: 10pt;">将交易划分成两组：高金额交易和非高金额交易(返回一个Map&lt;Boolean, List&lt;Transaction&gt;&gt;对象)</span><br /><span style="font-size: 10pt;">创建多层分组，例如先按交易发生的城市分组，再进一步按它们是否为高金额交易进行分组(返回一个Map&lt;String, Map&lt;Boolean, List&lt;Transaction&gt;&gt;&gt;)</span><br /><span style="font-size: 10pt;">兴奋吗？很好。让我们看看，你是如何使用Stream API和Collector来表达上述查询的。我们首先从一个简单的例子开始，这个例子要对这个流进行"总结"：计算它的平均值，最大值和最小值。然后我们再看看如何表达简单的分组，最后，再看看如何将Collector组合起来去创建更为强大的查询，例如多层分组。</span><br /><span style="font-size: 10pt;">总结。让我们用一些简单的例子来热身一下。在之前的文章中，你已经看到如何使用reduce方法去计算流中元素的数量，最小值，最大值和平均值，以及如何使用基本数据类型元素的流。有一些预定义的Collector类也能让你完成那些功能。例如，可以使用counting()方法去计算元素的数量，如清单11所示。</span><br /><strong><em><span style="font-size: 10pt;">清单11</span></em></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: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;howManyTransactions&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;transactions.stream().collect(counting());</span></div><span style="font-size: 10pt;">你可以使用summingDouble()，summingInt()和summingLong()分别对流中元素类型为Double，Int或Long的属性求和。在清单12中，我们计算出了所有交易的金额之和。</span><br /><em><strong><span style="font-size: 10pt;">清单12</span></strong></em><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; ">int</span><span style="color: #000000; ">&nbsp;totalValue&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;transactions.stream().collect(summingInt(Transaction::getValue));</span></div><span style="font-size: 10pt;">类似的，使用averagingDouble()，averagingInt()和averagingLong()去计算平均值，如清单13所示。</span><br /><em><strong><span style="font-size: 10pt;">清单13</span></strong></em><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; ">double</span><span style="color: #000000; ">&nbsp;average&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;transactions.stream().collect(averagingInt(Transaction::getValue));</span></div><span style="font-size: 10pt;">另外，使用maxBy()和minBy()方法，可以计算出流中值最大和最小的元素。但这里有一个问题：你需要为流中元素定义一个顺序，以能够对它们进行比较。这就是为什么maxBy（）和minBy()方法使用使用一个Comparator对象作为参数，图3表明了这一点。</span><br /><em><strong><span style="font-size: 10pt;">图3</span></strong></em><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140506_stream_03.png" alt="" border="0" height="226" width="290" /><br /><span style="font-size: 10pt;">在清单14的例子中，我们使用了静态方法comparing()，它将传入的函数作为参数，从中生成一个Comparator对象。该函数用于从流的元素中解析出用于进行比较的关键字。在这个例子中，通过使用交易金额作为比较的关键字，我们找到了那笔最高金额的交易。</span><br /><span style="font-size: 10pt;">清单14</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; ">Optional</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;highestTransaction&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(maxBy(comparing(Transaction::getValue)));</span></div><span style="font-size: 10pt;">还有一个reducing()方法，由它产生的Collector对象会把流中的所有元素归集在一起，对它们重复的应用同一个操作，直到产生结果。该方法与之前看过的reduce()方法在原理上一样的。例如，清单15展示了使用了基于reducing()方法的另一种方式去计算所有交易的金额之和。</span><br /><em><strong><span style="font-size: 10pt;">清单15</span></strong></em><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; ">int</span><span style="color: #000000; ">&nbsp;totalValue&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;transactions.stream().collect(reducing(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;Transaction::getValue,&nbsp;Integer::sum));</span></div><span style="font-size: 10pt;">reducing()方法使用三个参数：</span><br /><span style="font-size: 10pt;">初始值(如果流为空，则返回它)；此处，该值为0。</span><br /><span style="font-size: 10pt;">应用于流中每个元素的函数对象；此处，该函数会解析出每笔交易的金额。</span><br /><span style="font-size: 10pt;">将两个由解析函数生成的金额合并在一起的方法；此处，我们只是把金额加起来。</span><br /><span style="font-size: 10pt;">你可能会说，"等等，使用其它的流方法，如reduce()，max()和min()，我已经可以做到这些了。那么，你为什么还要给我看这些方法呢？"后面，你将会看到我们将Collector结合起来去构建更为复杂的查询(例如，对加法平均数进行分组)，所以，这也能更易于理解这些内建的Collector。</span><br /><span style="font-size: 10pt;">分组。这是一个普通的数据库查询操作，它使用属性去数据进行分组。例如，你也许想按币种对一组交易进行分组。若你使用如清单16所示的代码，通过显式的遍历去表达这个查询，那会是很痛苦的。</span><br /><em><strong><span style="font-size: 10pt;">清单16</span></strong></em><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; ">Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Currency,&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;transactionsByCurrencies&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;transaction&nbsp;:&nbsp;transactions)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;Currency&nbsp;currency&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;transaction.getCurrency();<br />&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionsForCurrency&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactionsByCurrencies.get(currency);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(transactionsForCurrency&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;transactionsForCurrency&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;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactionsByCurrencies.put(currency,&nbsp;transactionsForCurrency);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;transactionsForCurrency.add(transaction);<br />}</span></div><span style="font-size: 10pt;">你首先需要创建一个Map对象，它将收集所有的交易记录。然后，你需要遍历所有的交易记录，并解析出每笔交易的币种。在将交易记录使用一个值插入Map中之前，需要先检查一下，这个List是否已经创建过了，等等。</span><br /><span style="font-size: 10pt;">真是令人汗颜啊，因为我们想要是"按币种对交易进行分组"。为什么不得不涉及这么多代码呢？有好消息：有一个称为groupingBy()的Collector，它允许我们以简洁的方式来表达这个例子。我们可以使用清单17中的例子来表达这个相同的查询，现在代码的阅读更接近问题语句了。</span><br /><em><strong><span style="font-size: 10pt;">清单17</span></strong></em><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; ">Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Currency,&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;transactionsByCurrencies&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream().collect(groupingBy(Transaction::getCurrency));</span></div><span style="font-size: 10pt;">工厂方法groupingBy()使用一个函数对象作为参数，该函数会解析出用于分类交易记录的关键字。我们称为这个函数为分类函数。在此处，为了按币种对交易进行分组，我们传入一个方法引用，Transaction::getCurrency。图4演示了这个分组操作。</span><br /><em><strong><span style="font-size: 10pt;">图4</span></strong></em><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140506_stream_04.png" alt="" border="0" height="345" width="609" /><br /><span style="font-size: 10pt;">分割。有一个称为partitioningBy()的工厂方法，它可被视为一种特殊的groupingBy()方法。它使用一个谓语作为参数(该参数返回一个boolean值)，然后根据元素是否满足这个谓语对它们进行分组。换言之，它将组成流的交易分割成了结构Map&lt;Boolean, List&lt;Transaction&gt;&gt;。例如，如若你想将交易分割成两组--低廉的和昂贵的--你就可以像清单18那样去使用partitioningBy()产生的Collector。此例中的Lambda表达式，t-&gt;t.getValue() &gt; 1000，就是一个将交易分成低廉和昂贵的谓语。</span><br /><em><strong><span style="font-size: 10pt;">清单18</span></strong></em><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; ">Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Boolean,&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;partitionedTransactions&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream().collect(partitioningBy(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getValue()&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">));</span></div><span style="font-size: 10pt;">组合Collector。如果你熟悉SQL，你应该知道可以将GROUP BY与函数COUNT()和SUM()一块儿使用，以按币种和交易金额之和进行分组。那么，使用Stream API是否也可以实现相似的功能呢？当然可以。确切地说，有一个重载的groupingBy()方法，它使用另一个Collector作为第二个参数。这个额外的Collector对象用于定义在使用由groupingBy()产生的Collector时如何汇集所有与关键字相关的元素。</span><br /><span style="font-size: 10pt;">好吧，这听起来有些抽象，那么让我们看一个简单的例子。我们想基于每个城市的交易金额之和生成一个城市的Map对象(见清单19)。在此处，我们告诉groupingBy()方法使用getCity()方法作为分类方法。那么，得到的Map结果的Key就为城市。正常地，我们期望对Map中每个键所对应的值，即List&lt;Transaction&gt;对象，使用groupingBy()方法。</span><br /><em><strong><span style="font-size: 10pt;">清单19</span></strong></em><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; ">Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;cityToSum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream().collect(groupingBy(Transaction::getCity,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;summingInt(Transaction::getValue)));</span></div><span style="font-size: 10pt;">然后，我们却是传入了另一个Collector对象，它由summingInt()方法产生，该方法会将所有与特定城市相关的交易记录的金额加起来。结果，我们得到了一个Map&lt;String, Integer&gt;对象，它将每个城市与它们对应的所有交易的金额之和进行了映射。酷，不是吗？想想这个：基本的groupingBy(Transaction:getCity)方法其实就只是groupingBy(Transaction:getCity, toList())的简写。</span><br /><span style="font-size: 10pt;">让我们看看另一个例子。如果你想生成这样一个Map，它对每个城市与它的最大金额的交易记录进行映射，那要怎么做呢？你可能已经猜到了，我们可以重用前面过的由maxBy()方法产生的Collector，如清单20所示。</span><br /><em><strong><span style="font-size: 10pt;">清单20</span></strong></em><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; ">Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Optional</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;cityToHighestTransaction&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream().collect(groupingBy(Transaction::getCity,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxBy(comparing(Transaction::getValue))));</span></div><span style="font-size: 10pt;">你已经看到Stream API很善于表达，我们正在构建的一些十分有趣的查询都可以写的简洁些。你还能想象出回到从前去遍历地处理一个集合吗？让我们看一个更为复杂的例子，以结束这篇文章。你已看到groupingBy()方法可以将一个Collector对象作为参数，再根据进一步的分类规则去收集流中的元素。因为groupingBy()方法本身得到的也是一个Collector对象，那么通过传入另一个由groupingBy()方法得到的Collector对象，该Collector定义了第二级的分类规范，我们就能够创建多层次分组。</span><br /><span style="font-size: 10pt;">在清单21的代码中，先按城市对交易记录进行分组，再进一步对每个城市中的交易记录按币种进行分组，以得到每个城市中每个币种的所有交易记录的平均金额。图5就形象地展示了这种机制。</span><br /><em><strong><span style="font-size: 10pt;">清单21</span></strong></em><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; ">Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Currency,&nbsp;Double</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;cityByCurrencyToAverage&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream().collect(groupingBy(Transaction::getCity,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;groupingBy(Transaction::getCurrency,&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;averagingInt(Transaction::getValue))));</span></div><em><strong><span style="font-size: 10pt;">图5</span></strong></em><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140506_stream_05.png" alt="" border="0" height="419" width="609" /><br /><span style="font-size: 10pt;">创建你自己的Collector。到目前为止，我们展示的全部Collector都实现了接口java.util.stream.Collector。这就意味着，你可以实现自己的Collector，以"定制"归一操作。但是对于这个主题，再写一篇文章可能更合适一些，所以我们不会在本文中讨论这个问题。</span><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">在本文中，我们探讨了Stream API中的两个高级：flatMap和collect。它们是可以加到你的兵器库中的工具，可以用来表述丰富的数据处理查询。</span><br /><span style="font-size: 10pt;">特别地，你也已经看到了，collect()方法可被用于归纳，分组和分割操作。另外，这些操作还可能被结合在一起，去构建更为丰富的查询，例如"生产一个两层Map对象，它代表每个城市中每个币种的平均交易金额"。</span><br /><span style="font-size: 10pt;">然而，本文也没有查究到所有的内建Collector实现。请你去看看Collectors类，并试试其它的Collector实现，例如由mapping()，joining()和collectingAndThen()，也许你会发现它们也很有用。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/417011.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> 2014-08-15 19:57 <a href="http://www.blogjava.net/jiangshachina/archive/2014/08/15/417011.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用Java SE 8流处理数据(I)(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 27 Jul 2014 12:54:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/416235.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/416235.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/416235.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">利用Java SE 8流处理数据</span></strong><br /><span style="font-size: 10pt;">-- 使用Java流操作去表达复杂的数据查询</span></div><br /><span style="font-size: 10pt;">本文是<a href="http://www.oracle.com/technetwork/java/javamagazine/index.html">Java Magazine</a> 201403/04刊中的一篇文章，也是文章系列"利用Java SE 8流处理数据"中的第一篇，它概述了Java流的基本原理与基本应用，是一篇很好的Java Streams API的入门文章。(2014.07.27最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 没有集合对象，你会怎么样？几乎每个Java应用都会创建并处理集合。它们是许多编程任务的基础：集合使你能够对数据进行分组和处理。例如，你也许会创建一个关于银行交易的集合，该集合代表了某个用户的银行流水清单。然后，你可能想要处理整个集合去算出该用户花了多少钱。尽管数据处理十分重要，但Java在此方面表现的远不完美。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 首先，典型的集合处理模式与类SQL操作相似，诸如"查找"(如找出最大金额的那笔交易)或者"分组"(如，将与购买杂货相关的交易进行分组)。大部分数据库允许你以声明的形式去指定这些操作。例如，后面的SQL查询会让你找到那笔最大金额交易的ID："SELECT id, MAX(value) from transctions"。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如你所见，我们并不需要去实现如何计算最大值(例如，使用循环和一个变量去追踪这个最大值)。我们仅需要表达什么是我们想要的。这种基本思想就意味着，你不太需要担心去显式地实现这些查询--它们已经为你处理好了。为什么我们不能在处理集合时也这样做呢？你发现自己有多少次都是在一遍又一遍地使用循环去重复实现这些操作呢？</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 其次，我们如何才能更高效地去处理大型集合？理想情况下，在加速进行处理时，你会想到利用多核架构。然而，编写并行程序既困难又容易出错。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java SE 8赶来帮忙了！Java API的设计者们在升级API时引入了一种新的称之为Java流(流)的抽象，它允许你以声明形式去处理数据。另外，Java流可以利用到多核架构而不必编写一行多线程代码。听起来不错，不是吗？这就是本文章系列所要探究的主题。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java流能为我们做些什么呢？在探究这些细节之前，让我们先看一个例子，这样你才能对这种新的使用Java SE 8 Java流的编程风格有感觉。假设我们要找到所有类型为grocery的交易并返回它们的ID列表，并按交易金额的递减顺序对该列表进行排序。在Java SE 7中，我们应该会把清单1所示的程序那样去做。而在Java SE 8中，我们则会像清单2所示的那样去实现。</span><br /><em><strong><span style="font-size: 10pt;">清单1</span></strong></em><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; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;groceryTransactions&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;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;t:&nbsp;transactions){<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY){<br />&nbsp;&nbsp;&nbsp;&nbsp;groceryTransactions.add(t);<br />&nbsp;&nbsp;}<br />}<br />Collections.sort(groceryTransactions,&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Comparator(){<br />&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;compare(Transaction&nbsp;t1,&nbsp;Transaction&nbsp;t2){<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;t2.getValue().compareTo(t1.getValue());<br />&nbsp;&nbsp;}<br />});<br />List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionIds&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;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;t:&nbsp;groceryTransactions){<br />&nbsp;&nbsp;transactionsIds.add(t.getId());<br />}</span></div><br /><em><strong><span style="font-size: 10pt;">清单2</span></strong></em><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; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionsIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sorted(comparing(Transaction::getValue).reversed())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 图1形象地解释了那段Java SE 8程序。首先，我们调用List对象中的Java流()方法从交易列表(数据)中获取一个Java流对象。然后，多个操作(过滤，排序，映射，归集)链接在一起形成了一条线，这条线可以被看作构成了一条数据查询。</span><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140304_stream_01.png" alt="" border="0" height="144" width="505" /><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 那么如何并行地执行该程序呢？在Java SE 8中这很简单：只需要使用parallelJava流()方法去替换Java流()方法，如清单3所示。Java流 API会在内部对你的查询进行解构，并利用上你机器中的多核处理器。</span><br /><em><strong><span style="font-size: 10pt;">清单3</span></strong></em><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; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionsIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.parallelStream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sorted(comparing(Transaction::getValue).reversed())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在该关于Java SE 8 Java流的文章系列结束时，你将能够使用Java流 API编写出像清单3那样的功能强大的查询程序。</span><br /><br /><strong><span style="font-size: 12pt;">Java流入门</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 让我们先从一点理论开始。Java流的定义是什么？一个简短的定义就是"来自于一个数据源的能够支持聚合操作的一串元素"。让我们把它拆开来说：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>一串元素</strong>：Java流为一串特定类型值的集合提供了一个接口。然后，Java流实际上并不存储元素，它们会在需要时被用上。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>数据源</strong>：Java流要使用一个提供数据的源，诸如集合对象，数组或I/O资源。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>聚合操作</strong>：Java流支持类SQL的操作，以及来自于函数编程语言的通用操作，诸如过滤，映射，归一，查找，匹配，排序，等等。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另外，与集合操作非常不同的是，Java流操作拥有两项基本特质：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 管道：许多Java流操作会返回它们自己，这就使得这些操作能够链接在一起以组成一个大型管道。这样就可以进行一些诸如惰性和短路之类的优化，后面我们会进行探究。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 内部遍历：集合是显式地进行遍历(外部遍历)，但不同于集合，Java流是在幕后进行遍历。让我们重新看看之前的示例代码来解释这些原理。图2形象地解释了清单2的更多细节。</span><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140304_stream_02.png" alt="" border="0" height="364" width="595" /><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 首先，通过调用Java流()方法，我们从交易列表中得到了一个Java流对象。那么数据源就是交易列表，它将向Java流中提供一串元素。然后，我们对该Java流应用了一系列的聚合操作：过滤(提供一个谓语去过滤元素)，排序(提供一个比较器去对元素进行排序)，以及映射(解析出信息)。所有的操作都会返回该Java流，以便能够链接这些操作去组成一个管道，这可被看作是对数据源的一个查询。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在调用collect()操作之前，没有实际工作会被执行。collect()方法将开始处理这个管道以返回一个结果(某个不是Java流的对象，在此处，是一个List对象)。现在还不需要去关注collect()方法，我们会在以后的文章去一探究竟。此时，你会发现collect会将各种数据加工方法作为参数，将收集到的Java流元素归结为一个结果。此处，toList()就描述了一个将Java流对象转化为List对象的加工方法。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在探究与Java流有关的各个方法之前，最好是停下来深入思考一下Java流和集合之间观念上的不同之处。</span><br /><br /><strong><span style="font-size: 12pt;">Java流 vs. 集合</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 已有的Java集合概念与新的Java流概念都为一串元素提供了接口。那它们有何不同吗？简单地说，集合是关于数据的，而Java流是关于计算的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 想想这种情况，一部存储在DVD中的电影。这就是一个集合(可能是字节，可能是帧--在此处，我们不必关心这些)，因为它包含有全部的数据结构。现在再想想这种情况，这部电影被转化成了数据流，通过互联网去观看它。此时它就是一个(字节或帧的)流。流视频播放器只需要下载一些晚于用户当前所观看位置的帧就可以了。这样，你就可以在大部分值被计算出来之前先展示流开头处的值(想想流化一场现场直播的足球比赛)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 粗看之，集合与流的区别就是与何时处理数据有关。集合是内存中的数据结构，它包含有当前数据结构中的全部值--将所有元素加入到集合之前，必须先对所有元素进行处理，相反地，Java流只是逻辑上固定的数据结构，它里面的元素只会根据需要进行处理。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用Collection接口，要求用户实现遍历(例如，使用增强的for循环，即foreach)；这被称之为外部循环。相反地，Stream类库使用内部遍历--它已经为你实现好了遍历，它会关心存储流的结果值的位置；你仅需要提供一个函数告诉它要做些什么就行了。清单4(对集合的外部遍历)和清单5(对Java流的内部遍历)中的代码形象地展示了这一不同之处。</span><br /><em><strong><span style="font-size: 10pt;">清单4</span></strong></em><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; ">List</span><span style="color: #000000; ">&lt;Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionIds&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;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;t:&nbsp;transactions){<br />&nbsp;&nbsp;&nbsp;&nbsp;transactionIds.add(t.getId());<br />}</span></div><br /><strong><em><span style="font-size: 10pt;">清单5</span></em></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; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单4中，我们显式且顺序地遍历了交易列表，抽取了每个交易ID，然后将它加到一个收集器中。相反地，当使用流时，没有显式的遍历。清单5中的代码构建了一个查询，其中的map操作被设定为一个参数，它会抽取交易ID，然后collect操作会把结果Stream对象转化成一个List对象。</span><br /><span style="font-size: 10pt;">你现在应该知道什么是Java流，以及如何去使用它。现在让我们看看Java流所支持的操作之间的区别，这样你就能构建自己的数据查询了。</span><br /><strong><br /><span style="font-size: 12pt;">Java流操作：使用流去处理数据</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; java.util.stream.Stream接口定义了许多操作，它们可被归集为两类。在图1所示的例子中，你可以看到如下操作：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 过滤，排序和映射，它们可被连接在一起组成一个管道</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 收集，它关闭了这个管道并返回结果</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 能够被连接在一起的Java流操作被称为中间操作。这些操作之所以能被连接在一起，是因为它们都会返回Stream对象。这些操作从这个管道中返回结果，结果的类型可以是List，Integer，甚至是void(任何Stream以外的类型)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你也许很好奇为什么这种区别很重要。是这样的，在这个Java流管道的最终操作被调用之前，中间操作并不会执行任何处理；它们是"惰性"方法。这是因为中间方法经常会被"合并"，在最终操作中它们会被合成为单一的执行路径。</span><br /><em><strong><span style="font-size: 10pt;">清单6</span></strong></em><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; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;numbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.asList(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">5</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">6</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">7</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">8</span><span style="color: #000000; ">);<br />List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;twoEvenSquares&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;numbers.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(n&nbsp;</span><span style="color: #000000; ">-&gt;</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;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">filtering&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; "> n);<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; ">return</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&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;&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;.map(n&nbsp;</span><span style="color: #000000; ">-&gt;</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;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">mapping&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; "> n);<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; ">return</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;n;<br />&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;.limit(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">例如，思考下清单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; ">filtering&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; "><br />filtering&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />mapping&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />filtering&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; "><br />filtering&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; "><br />mapping&nbsp;</span><span style="color: #000000; ">4</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这是因为limit(2)使用了短路；我们只需要处理流的一部分，而不是全部，去得到一个结果。这就类似于测评一个由and操作符关联起来的大型布尔表达式链：一旦某个表达式返回了false，那么就可以认为整个表达式链就是false，而不必测评所有的表达式了。在这个例子中，limit()方法将返回的Java流的长度限定为2。另外，filter与map被合并在了同一条执行路径中了。</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 归纳一下到目前为止，在使用Java流时我们所学到的内容，总言之，涉及三个方面：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个数据源(例如一个集合)，对它执行查询</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个中间操作的链，它组成一个流的管道</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个最终操作，它执行流的管道并产生结果</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 现在让我们看看Java流所支持的一些操作。参考java.util.stream.Stream接口可以得到这些方法的完整清单，再看看本文末尾所给出的资源，它包含有更多的例子。</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>过滤</strong>。有多种方法可以对流中的元素进行过滤：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; filter(Predicate)：使用一个谓语(java.util.function.Predicate)作为参数，它会返回一个包含所有匹配给定谓语条件元素的Java流。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; distinct：返回一个包含有唯一元素的Java流。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; limit(n)：返回的流的长度不能超过n。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; skip(n)：返回的流将不包括前n个元素。</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>查找与匹配</strong>。一个通用的数据处理模式就要考虑一些元素是否匹配给定的属性。你可以使用anyMatch，allMatch和noneMatch方法帮你做到这一点。这些方法都会使用一个谓语参数并返回boolean值作为结果(所以，它们是最终操作)。例如，使用allMatch去查出交易流中所有金额大于100的交易，如清单7所示。</span><br /><em><strong><span style="font-size: 10pt;">清单7</span></strong></em><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; ">boolean</span><span style="color: #000000; ">&nbsp;expensive&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.allMatch(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getValue()&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">100</span><span style="color: #000000; ">);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另外，Stream接口提供了方法findFirst和findAny，以取出流中任一元素。它们可以与其它的流操作，如filter，结合起来使用。findFirst和findAny都会返回一个Optinal对象(见清单8)。</span><br /><em><strong><span style="font-size: 10pt;">清单8</span></strong></em><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; ">Optional</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.findAny(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY);</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">Optional&lt;T&gt;类(java.util.Optional)是一个容器类，它代表一个存在或不存在的值。清单8中的程序，findAny方法可能没有找到任何类型为grocery的交易。Optional类包含多个方法去测试一个元素是否存在。例如，如果交易存在，通过使用ifPresent方法，我们可以选择一个操作去应用这个Optaional对象，如清单9所示(此处只是打印交易)。</span><br /><em><strong><span style="font-size: 10pt;">清单9</span></strong></em><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; ">transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.findAny(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.ifPresent(System.out::println);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>映射</strong>。Java流支持map方法，它使用一个函数(java.util.function.Function)作为参数，将流元素投影到其它形式。这个函数会被应用到每个元素，并将元素"映射"到新的元素。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 例如，你可能会想到使用它去抽取流中每个元素的信息。在清单10的例子中，我们返回了一个列表中每个字的长度。</span><br /><em><strong><span style="font-size: 10pt;">清单10</span></strong></em><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; ">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;words&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.asList(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Oracle</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: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Magazine</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;wordLengths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;words.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(String::length)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>归一</strong>。到目前为止，我们已见过的最终操作会返回boolean(allMatch等等)，void(forEach)或Optaional对象(findAny等等)。我们也使用collect方法将Stream对象中的所有元素放到一个List对象中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 然而，你也可以将流中的元素放到一个查询中，该查询可表达更为复杂的数据处理，例如"拥有最大ID"或者"算出所以交易金额的和"。这就可能对Java流用上reduce方法，该方法会对每个元素重复地应用一个操作(例如，加上两个数字)，直到生成结果。在函数式编程中，这常被称为折叠操作。因为该操作可被看作重复地"折叠"一张很长的纸(Stream对象)，直到这张纸的面积变得只有一点儿了。这就是折叠操作的结果。</span><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: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;sum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;x&nbsp;:&nbsp;numbers)&nbsp;{<br />&nbsp;&nbsp;sum&nbsp;</span><span style="color: #000000; ">+=</span><span style="color: #000000; ">&nbsp;x;<br />}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 列表中的每一个数字元素都被迭代地组合在一起，并使用一个额外的操作符去产生结果。本质上，我们就是把一组数字"归一"成一个数字。在这段代码中有两个参数：数字和变量的初始值，即该例中的0，以及用于合并所有元素的操作符，即本例中的+。</span><br /><em><strong><span style="font-size: 10pt;">清单11</span></strong></em><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; ">int</span><span style="color: #000000; ">&nbsp;sum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;numbers.stream().reduce(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;(a,&nbsp;b)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;b);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 对Java流使用reduce方法，我们可以计算出流中的所有元素值之和，如清单11所示。reduce方法使用两个参数：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 初始值，0</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; BinaryOperation&lt;T&gt;，合并两个元素，并产生一个新值</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; reduce方法本质上就是重复应用模式的抽象。其它的查询，如"计算产量"或"计算最大值"(如清单12所示)则是reduce方法的特别实例。</span><br /><em><strong><span style="font-size: 10pt;">清单12</span></strong></em><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; ">int</span><span style="color: #000000; ">&nbsp;product&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;numbers.stream().reduce(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;(a,&nbsp;b)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;b);<br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;product&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;numbers.stream().reduce(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;Integer::max);</span></div><br /><strong><span style="font-size: 12pt;">数字流</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你已经看到可以使用reduce方法去计算整数流的和。然后，这也是有成本的：我们重复执行了许多拆箱操作以将Integer对象加到一起。如果我们能调用一个sum方法，使程序的意图更为明显，就像清单13那样，岂不是更好？</span><br /><em><strong><span style="font-size: 10pt;">清单13</span></strong></em><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; ">int</span><span style="color: #000000; ">&nbsp;statement&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getValue)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sum();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;error&nbsp;since&nbsp;Stream&nbsp;has&nbsp;no&nbsp;sum&nbsp;method</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">Java 8引入的三个特定的基本数据类型的流接口来应对这个问题--IntStream，DoubleStream和LongStream--它们专注于元素分别为int，double和long型的Java流。将一个流转化为特定类型的流，你最常使用的方法就是mapToInt，mapToDouble和mapToLong。这些方法与我们较早前看到的map方法是一样的，但它们会返回特定类型的Stream对象，而不是Stream&lt;T&gt;对象。例如，我们可以改进下清单13中的代码，如清单14所示那样。你也可以使用装箱操作将一个基本数据类型的流转化成一个使用包装对象的流。</span><br /><em><strong><span style="font-size: 10pt;">清单14</span></strong></em><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; ">int</span><span style="color: #000000; ">&nbsp;statementSum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.mapToInt(Transaction::getValue)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sum();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;works!</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 最后，数字流的另一种有用的形式是数字区间。比如，你可能想生成介于1到100之间的所有数字。为了帮助生成这种区间，Java SE 8在IntStream，DoubleStream和LongStream中分别引入了两个静态方法：range和rangeClosed。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这两个方法都会使用两个参数，第一个参数是起始值，第二个参数是终止值。但是range方法生成的区间不会包含终止值本身，但rangeClosed生成的区间则会包含。清单15是一个使用rangeClosed方法的例子，它返回一个包含有全部介于10到30之间奇数的流。</span><br /><em><strong><span style="font-size: 10pt;">清单15</span></strong></em><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; ">IntStream&nbsp;oddNumbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;IntStream.rangeClosed(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">30</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(n&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);</span></div><br /><strong><span style="font-size: 12pt;">构建流</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 有多种途径可以去构建一个流。你已经看过如何从集合对象中构建流。另外，我们还操控过数字流。你也可以从值，数组或文件中去创建流。另外，你甚至于可以从一个函数中生成无限流。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 可以直截了当地从值或数组中创建流：只需要使用一些静态方法即可，对于值，是Stream.of()；而对于数组，则要调用Arrays.stream()。如清单16所示。</span><br /><em><strong><span style="font-size: 10pt;">清单16</span></strong></em><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; ">Stream</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;numbersFromValues&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Stream.of(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">[]&nbsp;numbers&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; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">};<br />IntStream&nbsp;numbersFromArray&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.stream(numbers);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你也可以将一个文件转化为其内容行的流，使用静态方法Files.lines()即可。清单17就使用该方法计算了文件中行的数量。</span><br /><em><strong><span style="font-size: 10pt;">清单17</span></strong></em><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; ">long</span><span style="color: #000000; ">&nbsp;numberOfLines&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;Files.lines(Paths.get(&#8220;yourFile.txt&#8221;),&nbsp;Charset.defaultCharset())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.count();</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>无限流</strong>。最后，在总结本文之前，有一个令人非常兴奋的主意。到现在为止，你应该理解到流中的元素是按需生成的。有两个静态方法--Stream.iterate()和Stream.generate()--可以让你从一个函数中创建流。然而，因为被使用的元素是按需生成的，所以这两个方法可以"永远地"生成元素。这就是为什么我们称它为无限流：它就是没有固定大小的流，但它做的事情与一个从固定集合生成的流是一样的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单18就是一个使用iterate方法的例子，它会包含10的所有倍数。iterate方法使用一个起始值(此处的0)和一个Lambda表达式(类型为UnaryOperator&lt;T&gt;)去顺序地生成每一个新值。</span><br /><em><strong><span style="font-size: 10pt;">清单18</span></strong></em><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; ">Stream</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;numbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Stream.iterate(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;n&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">10</span><span style="color: #000000; ">);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我们也可以使用limit方法，以从一个无限流中得到一个固定流。如清单19所示，可以将流的长度限制为5。</span><br /><em><strong><span style="font-size: 10pt;">清单19</span></strong></em><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; ">numbers.limit(</span><span style="color: #000000; ">5</span><span style="color: #000000; ">).forEach(System.out::println);&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;0,&nbsp;10,&nbsp;20,&nbsp;30,&nbsp;40</span></div><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java SE 8引入了Streams API，它让你能够表达更为复杂的数据处理查询。在本文中，你已见到流可以支持许多操作，诸如过滤，映射，归一和迭代，把它们结合在一起可以写出简洁的、更富表现力的数据处理查询。这种新的编程方法远不同于Java SE 8之前的集合处理。但是，它有许多好处。首先，它利用到了诸如惰性或短路这样的技术，以优化数据处理查询的性能。其次，能够自动地利用上多核架构，以并行地处理流。在本文章系统的第二部分中，我们将探索更高级的操作，例如flatMap和collect。请继续关注。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/416235.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> 2014-07-27 20:54 <a href="http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 8的语言变化(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 19 Apr 2014 15:48:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/412695.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/412695.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/412695.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java 8的语言变化</span></strong><br /><div align="center"><span style="font-size: 10pt;">--理解Lambda表达式和变化的接口类是如何使Java 8成为新的语言</span></div></div><span style="font-size: 10pt;">本文是IBM developerWorks中的一篇介绍Java 8关键新特性的<a href="http://www.ibm.com/developerworks/java/library/j-java8lambdas/index.html">文章</a>，它主要关注Lambda表达式和改进的接口。(2014.04.19最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8包含了一组重要的新的语言特性，使你能够更方便地构造程序。Lambda表达为内联的代码块定义了一种新的语法，给予你与匿名内部类相同的灵活性，但又没有那么多模板代码。接口的改变使得能够为已有接口加入新的特性，而不必打破现有代码的兼容性。了解这些语言变化是怎样一起工作的，请阅读本系列另一篇文章"Java 8并发基础"，可以看到如何在Java 8流中使用Lambda。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8的最大改变就是增加了对Lambda表达式的支持。Lambda表达式一种通过引用进行传递的代码块。它类似于某些其它语言的闭包：代码实现了一个功能，可以传入一个或多个参数，还可以返回一个结果值。闭包被定义在一个上下文中，它可以访问(在Lambda中是只读访问)上下文中的值。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如果你不熟悉闭包，也不必担心。Java 8的Lambda表达式是几乎每个Java开发者都熟悉的匿名内部类的一个高效版规范。如果你只想在一个位置实现一个接口，或是创建一个基类的子类时，匿名内部类为此提供了一种内联实现。Lambda表达式也用于相同的方式，但是它使用一种缩略的语法，使得这些实现比一个标准的内部类定义更为简洁。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在本文中，你将看到如何在不同的场景下使用Lambda表达式，并且你会学到与Java接口定义相关的扩展。在本文章的姊妹篇JVM并发系列的"Java 8并发基础"一文中，可以看到更多使用Lambda表达式的例子，包括在Java 8流特性中的应用。</span><br /><br /><strong><span style="font-size: 12pt;">进入Lambda</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Lambda表达式就是Java 8所称的函数接口的实现：一个接口只定义一个抽象方法。只定义一个抽象方法的限制是非常重要的，因为Lambda表达式的语法并不会使用方法名。相反，该表达式会使用动态类型识别(匹配参数和返回类型，很多动态语言都这么做)去保证提供的Lambda能够与期望的接口方法兼容。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单1所示的简单例子中，一个Lambda表达式被用来对Name实例进行排序。main()方法中的第一个代码块使用一个匿名内部类去实现Comparator&lt;Name&gt;接口，第二个语句块则使用Lambda表达式。</span><br /><strong><span style="font-size: 10pt;">清单1. 比较Lambda表达式与匿名内部类</span></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: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Name&nbsp;{<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;String&nbsp;firstName;<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;String&nbsp;lastName;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Name(String&nbsp;first,&nbsp;String&nbsp;last)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;first;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;last;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;only&nbsp;needed&nbsp;for&nbsp;chained&nbsp;comparator</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;getFirstName()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;firstName;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;only&nbsp;needed&nbsp;for&nbsp;chained&nbsp;comparator</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;getLastName()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;lastName;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;only&nbsp;needed&nbsp;for&nbsp;direct&nbsp;comparator&nbsp;(not&nbsp;for&nbsp;chained&nbsp;comparator)</span><span style="color: #008000; "><br /></span><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; ">int</span><span style="color: #000000; ">&nbsp;compareTo(Name&nbsp;other)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;diff&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;lastName.compareTo(other.lastName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(diff&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;diff&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;firstName.compareTo(other.firstName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;diff;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}<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;NameSort&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<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;Name[]&nbsp;NAMES&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Name[]&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Name(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Sally</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Smith</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),<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 />&nbsp;&nbsp;&nbsp;&nbsp;<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; ">void</span><span style="color: #000000; ">&nbsp;printNames(String&nbsp;caption,&nbsp;Name[]&nbsp;names)&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; ">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 /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;anonymous&nbsp;inner&nbsp;class</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Name[]&nbsp;copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Arrays.sort(copy,&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">()&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; ">int</span><span style="color: #000000; ">&nbsp;compare(Name&nbsp;a,&nbsp;Name&nbsp;b)&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;a.compareTo(b);<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;printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;anonymous&nbsp;inner&nbsp;class:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;lambda&nbsp;expression</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Arrays.sort(copy,&nbsp;(a,&nbsp;b)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;a.compareTo(b));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;lambda&nbsp;expression:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);<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 />}</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单1中，Lambda被用于取代匿名内部类。这种匿名内部类在应用中非常普遍，所以Lambda表达式很快就赢得了Java8程序员们的青睐。(在本例中，同时使用匿名内部类和Lambda表达式去实现Name类中的一个方法，以方便对这两种方法进行比较。如果在Lambda中对compareTo()方法进行内联的话，该表达式将会更加简洁。)</span><br /><br /><strong><span style="font-size: 12pt;">标准的函数式接口</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 为了应用Lambda，新的包java.util.function中定义了广泛的函数式接口。它们被归结为如下几个类别：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 函数：使用一个参数，基于参数的值返回结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 谓语：使用一个参数，基于参数的值返回布尔结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 双函数：使用两个参数，基于参数的值返回结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 供应器：不使用任何参数，但会返回结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 消费者：使用一个参数，但不返回任何结果。</span><br /><span style="font-size: 10pt;">多数类别都包含多个不同的变体，以便能够作用于基本数据类型的参数和返回值。许多接口所定义的方法都可被用于组合对象，如清单2所示：</span><br /><strong><span style="font-size: 10pt;">清单2. 组合谓语</span></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: #008000; ">//</span><span style="color: #008000; ">&nbsp;use&nbsp;predicate&nbsp;composition&nbsp;to&nbsp;remove&nbsp;matching&nbsp;names</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;list&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;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(Name&nbsp;name&nbsp;:&nbsp;NAMES)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;list.add(name);<br />}<br />Predicate</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;pred1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Sally</span><span style="color: #000000; ">"</span><span style="color: #000000; ">.equals(name.firstName);<br />Predicate</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;pred2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Queue</span><span style="color: #000000; ">"</span><span style="color: #000000; ">.equals(name.lastName);<br />list.removeIf(pred1.or(pred2));<br />printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;filtered&nbsp;by&nbsp;predicate:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;list.toArray(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Name[list.size()]));</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单2定义了一对Predicate&lt;Name&gt;变量，一个用于匹配名为Sally的名字，另一个用于匹配姓为Queue的名字。调用方法pred1.or(pred2)会构造一个组合谓语，该谓语先后使用了两个谓语，当它们中的任何一个返回true时，这个组合谓语就将返回true(这就相当于早期Java中的逻辑操作符||)。List.removeIf()方法就应用这个组合谓语去删除列表中的匹配名字。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8定义了许多有用的java.util.function包中接口的组合接口，但这种组合并不都是一样的。所有的谓语的变体(DoublePredicate，IntPredicate，LongPredicate和Predicate&lt;T&gt;)都定义了相同的组合与修改方法：and()，negate()和or()。但是Function&lt;T&gt;的基本数据类型变体就没有定义任何组合与修改方法。如果你拥有使用函数式编程语言的经验，那么你可能就发会发现这些不同之处和奇怪的忽略。</span><br /><br /><strong><span style="font-size: 12pt;">改变接口</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在Java 8中，接口(如清单1的Comparator)的结构已发生了改变，部分原因是为了让Lambda更好用。Java 8之前的接口只能定义常量，以及必须被实现的抽象方法。而Java 8中的接口则能够定义静态与默认方法。接口中的静态方法与抽象类中的静态方法是完全一样的。默认方法则更像旧式的接口方法，但提供了该方法的一个实现。该方法实现可用于该接口的实现类，除非它被实现类覆盖掉了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 默认方法的一个重要特性就是它可以被加入到已有接口中，但又不会破坏已使用了这些接口的代码的兼容性(除非已有代码恰巧使用了相同名字的方法，并且其目的与默认方法不同)。这是一个非常强大的功能，Java 8的设计者们利用这一特性为许多已有Java类库加入了对Lambda表达式的支持。清单3就展示了这样的一个例子，它是清单1中对名字进行排序的第三种实现方式。</span><br /><strong><span style="font-size: 10pt;">清单3. 键-提取比较器链</span></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: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;key-extractor&nbsp;lambdas</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.lastName);<br />comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;comp.thenComparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.firstName);<br />Arrays.sort(copy,&nbsp;comp);<br />printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;key&nbsp;extractor&nbsp;comparator:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单3首先展示了如何使用新的Comparator.comparing()静态方法去创建一个基于键-提取(Key-Extraction) Lambda的比较器(从技术上看，键-提取Lambda就是java.util.function.Function&lt;T,R&gt;接口的一个实例，它返回的比较器的类型适用于类型T，而提取的键的类型R则要实现Comparable接口)。它还展示了如何使用新的Comparator.thenComparing()默认方法去组合使用比较器，清单3就返回了一个新的比较器，它会先按姓排序，再按名排序。</span><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; ">Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.lastName)<br />&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.firstName);</span></div><span style="font-size: 10pt;">但不幸地是，Java 8的类型推导不允许这么做。为从静态方法中得到期望类型的结果，你需要为编译器提供更多的信息，可以使用如下任何一种形式：</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; ">Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;com1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing((Name&nbsp;name1)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name1.lastName)<br />&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(name2&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name2.firstName);<br />Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;com2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name,String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">comparing(name1&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name1.lastName)<br />&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(name2&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name2.firstName);</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 第一种方式在Lambda表达式中加入参数的类型：(Name name1) -&gt; name1.lastName。有了这个辅助信息，编译才能知道下面它该做些什么。第二种方式是告诉编译器要传递给Function接口(在此处，该接口通过Lambda表达式实现)中comparing()方法的泛型变量T和R的类型。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 能够方便地构建比较器以及比较器链是Java 8中很有用的特性，但它的代价是增加了复杂度。Java 7的Comparator接口定义了两个方法(compare()方法，以及遍布于每个对象中的equals()方法)。而在Java 8中，该接口则定义了18个方法(除了原有的2个方法，还新加入了9个静态方法和7个默认方法)。你将发现，为了能够使用Lambda而造成的这种接口膨胀会重现于相当一部分Java标准类库中。</span><br /><br /><strong><span style="font-size: 12pt;">像Lambda那样使用已有方法</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如果一个存在的方法已经实现了你的需求，你可以直接使用一个方法引用对它进行传递。清单4展示了这种方法。</span><br /><strong><span style="font-size: 10pt;">清单4. 对已有方法使用Lambda</span></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; "><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;existing&nbsp;methods&nbsp;as&nbsp;lambdas</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing(Name::getLastName).thenComparing(Name::getFirstName);<br />Arrays.sort(copy,&nbsp;comp);<br />printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;existing&nbsp;methods&nbsp;as&nbsp;lambdas:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单4做着与清单3相同的事情，但它使用了已有方法。使用Java 8的形为"类名:方法名"的方法引用语法，你可以使用任意方法，就像Lambda表达式那样。其效果就与你定义一个Lambda表达式去调用该方法一样。对类的静态方法，特定对象或Lambda输入类型的实例方法(如在清单4中，getFirstName()和getLastName()方法就是Name类的实例方法)，以及类构造器，都可以使用方法引用。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 方法引用不仅方便，因为它们比使用Lambda表达式可能更高效，而且为编译器提供了更好的类型信息(这也就是为什么在上一节的Lambda中使用.thenComparing()构造Comparator会出现问题，而在清单4却能正常工作)。如果既可以使用对已有方法的方法引用，也可以使用Lambda表达式，请使用前者。</span><br /><br /><strong><span style="font-size: 12pt;">捕获与非捕获Lambda</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你在本文中已见过的Lambda表达式都是非捕获的，意即，它们都是把传入的值当作接口方法参数使用的简单Lambda表达式。Java 8的捕获Lambda表达式则是使用外围环境中的值。捕获Lambda类似于某些JVM语言(如Scala)使用的闭包，但Java 8的实现与之有所不同，因为来自在外围环境中的值必须声明为final。也就是说，这些值要么确实为final(就如同以前的Java版本中由匿名内部类所引用的值)，要么在外围环境中不会被修改。这一规范适用于Lambda表达式和匿名内部类。有一些方法可以绕过对值的final限制。例如，在Lambda中仅使用特定变量的当前值，你可以添加一个新的方法，把这些值作为方法参数，再将捕获的值(以恰当的接口引用这种形式)返回给Lambda。如果期望一个Lambda去修改外围环境中的值，那么可以用一个可修改的持有器类(Holder)对这些值进行包装。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 相比于捕获Lambda，可以更高效地处理非捕获Lambda，那是因为编译能够把它生成为类中的静态方法，而运行时环境可以直接内联的调用这些方法。捕获Lambda也许低效一些，但在相同上下文环境中它至少可以表现的和匿名内部类一样好。</span><br /><br /><strong><span style="font-size: 12pt;">幕后的Lambda</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Lambda表达式看起来像匿名内部类，但它们的实现方法不同。Java的内部类有很多构造器；每个内部类都会有一个字节码级别的独立类文件。这就会产生大量的重复代码(大部分是在常量池实体中)，类加载时会造成大量的运行时开销，哪怕只有少量的代码也会有如此后果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8没有为Lambda生成独立的类文件，而是使用了在Java 7中引入的invokedynamic字节码指令。invokedynamic作用于一个启动方法，当该方法第一次被调用时它会转而去创建Lambda表达式的实现。然后，该实现会被返回并被直接调用。这样就避免了独立类文件带来的空间开销，以及加载类的大量运行时开销。确切地说，Lambda功能的实现被丢给了启动程序。目前Java 8生成的启动程序会在运行时为Lambda创建一个新类，但在将来会使用不同的方法去实现。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8使用的优化使得通过invokedynamic指令实现的Lambda在实际中运行正常。多数其它的JVM语言，包括Scala (2.10.x)，都会为闭包使用编译器生成的内部类。在将来，这些语言可能会转而使用invokedynamic指令，以便利用到Java 8(及其后继版本)的优化。</span><br /><br /><strong><span style="font-size: 12pt;">Lambda的局限</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如在本文开始时我所提到的，Lambda表达式总是某些特殊函数式接口的实现。你可以仅把Lambda当作接口引用去传递，而对于其它的接口实现，你也可以只是把Lambda当作这些特定接口去使用。清单5展示了这种局限性，在该示例使用了一对相同的(名称除外)函数式接口。Java 8编译接受String::lenght来作为这两个接口的Lambda实现。但是，在一个Lambd表达式被定义为第一个接口的实例之后，它不能够用于第二个接口的实例。</span><br /><strong><span style="font-size: 10pt;">清单5. Lambda的局限</span></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: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;A&nbsp;{<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;valueA(String&nbsp;s);<br />}<br /></span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;B&nbsp;{<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;valueB(String&nbsp;s);<br />}<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;A&nbsp;a&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;String::length;<br />&nbsp;&nbsp;&nbsp;&nbsp;B&nbsp;b&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;String::length;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;compiler&nbsp;error!<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;b&nbsp;=&nbsp;a;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;ClassCastException&nbsp;at&nbsp;runtime!<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;b&nbsp;=&nbsp;(B)a;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;works,&nbsp;using&nbsp;a&nbsp;method&nbsp;reference</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;a::valueA;<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(b.valueB(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">abc</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />}</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 任何对Java接口概念有所了解的人都不会对清单5中的程序感到惊讶，因为那就是Java接口一直所做的事情(除了最后一点，那是Java 8新引入的方法引用)。但是使用其它函数式编程语言，例如Scala，的开发者们则会认为接口的这种限制是不自然的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 函数式编程语言是用函数类型，而不是接口，去定义变量。在这些编程语言中会很普遍的使用高级函数：把函数作为参数传递给其它的函数，或者把函数当作值去返回。其结果就是你会得到比Lambda更为灵活的编程风格，这包括使用函数去组合其它函数以构建语句块的能力。因为Java 8没有定义函数类型，你不能使用这种方法去组合Lambda表达式。你可以组合接口(如清单3所示)，但只能是与Java 8中已写好的那些接口相关的特定接口。仅在新的java.util.function包内，就特殊设定了43个接口去使用Lambda。把它们加入到数以百计的已有接口中，你将看到这种方法在组合接口时总是会有严重的限制。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用接口而不是在向Java中引入函数类型是一个精妙的选择。这样就在防止对Java类库进行重大改动的同时也能够对已有类库使用Lambda表达式。它的坏作用就是对Java 8造成了极大的限制，它只能称为"接口编程"或是类函数式编程，而不是真正的函数式编程。但依靠JVM上其它语言，也包括函数式语言，的优点，这些限制并不可怕。</span><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Lambda是Java语言的最主要扩展，伴着它们的兄弟新特性--方法引用，随着程序被移植到Java 8，Lambda将很快成为所有Java开发者不可或缺的工具。当与Java 8流结合起来时，Lambda就特别有用。查看文章"<a href="http://www.ibm.com/developerworks/library/j-jvmc2/index.html">JVM并发: Java 8并发基础</a>"，可以了解到将Lambda和流结合起来使用是如何简化并发编程以及提高程序效率的。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/412695.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> 2014-04-19 23:48 <a href="http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java流的8个特性(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Fri, 28 Feb 2014 07:25:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/410455.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/410455.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/410455.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Java流的8个特性</span></strong></div><span style="font-size: 10pt;">本文是稍早前java.net推荐的一篇</span><a href="http://speling.shemnon.com/blog/2014/02/11/8-cool-things-about-java-streams/"><span style="font-size: 10pt;">博文</span></a><span style="font-size: 10pt;">，描述了Java流的8个有用的特性。(2014.03.07最后更新)</span><br /><br /><span style="font-size: 10pt;">Lamba表达式是Java8到目前为止最棒的特性。但我认为有一个秘密武器展示了Lambda这一"语法糖"在提高代码可读性和可写性方面是何其的强大。当你在改进代码的表现力时，那么在对代码的理解方面你就上升到了新的境界，这能使最笨拙的工作变得简单。</span><br /><br /><span style="font-size: 10pt;">是什么秘密武器呢？就是Java Stream API。最近我参与了一个在线比赛，就是在一个对性能要求较高的环境中简单地使用Java Stream。令我惊讶的是，这个API让编写主要的循环程序变得十分简单，而且能很好地适应我所做出的众多变化。下面就是我所学到的8个特性。</span><br /><br /><strong style="font-size: 10pt;">1. Java流不需要Lambda表达式</strong><br />尽管这个API确实从Lambda表达式中获准良多，但你并不必非得使用Lambda。你可以回过去使用匿名内部类，但为什么要这么做呢？较可能的场景是，使用一个方法引用(例如Integer::valueOf)，或者一个实例对象。使用方法引用可将复杂的多行逻辑置于循环体之外，就如你在优化一个hash set查找时所看到的。而实例对象可用于实现"四人帮"的策略模式。但请不要使用匿名内部类，除非你不得不这么做。<br /><br /><strong>2. 窥入流内进行调试</strong><br />你可以在流的任何位置放入你所想加进去的媒质，这个媒质称为peek。该操作使用了一个消费者对象，并期望不产生任何结果，因为Lambda一般只返回空。我喜欢把peek用于向系统发送调试信息，就如<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; ">.peek(System.out::println)<br />.peek(it&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;System.out.printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">it&nbsp;is&nbsp;%s%n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;it)</span></div><br /><strong>3. 流化随机成员</strong><br />流并不局限于集合或数组，甚至是固定链表。如果你能创建一个Iterator或Supplier Lambda来创建流中的值，然后你就可以使用类java.util.stream.StreamSupport中的方法来创建一个流了。可以设想一个使用持续测量值，如内存消耗量或网络吞量，来驱动的流。<br /><br /><strong>4. 流化随机数</strong><br />如果你正在寻找一个简单的随机数，例如可以通过java.util.Random，这个类现在有了三个新的set方法ints()，longs()和doubles()来创建流。这些方法的重载版本可以让你设置边界，随机种子以及流中随机数的总量。<br /><br /><strong>5. 流化I/O Reader</strong><br />Java程序员的另一个常见工作就是一行一行地解析文件。现在java.io.BufferedReader有了一个新方法lines()，它会将I/O流转化为一个字符串流，以便于流的处理。<br /><br /><strong>6. 流化文件树</strong><br />如果访问的文件并不是你的菜，那就试试访问一个文件树会怎么样？类java.nio.file.Files中有几个方法都可以返回流。list()方法将列出一个目录下的所有文件，walk()方法将会递归地做到这一点，而filter()方法也会递归地访问这些文件，但会使用一些属性来进行过滤(当你有一个Path对象，有些事情会变得复杂起来)。你依然可以使用lines(Path)方法来通过流去获取内容。<br /><br /><strong>7. 流化复杂文本</strong><br />如果你依然念念不忘文本处理，但内容并不是基于行，那么就可以在java.util.regex.Pattern实例中使用splitAsStream(CharSequence)方法。这对于处理有数百万列的CSV文件或CLASSPATH十分有用。<br /><br /><strong>8. 流化ZIP文件</strong><br />说到对长CLASSPATH的搜索，你也可以很简单地调用名为stream的方法来流化java.util.zip.ZipFiles和java.util.jar.JarFiles，它会相应地返回一个ZipEntry或JarEntry实例。<br /><br />如果你都已经干过这些事了，那么你肯定知道它们并不是Java流的基本用途。不过将来会有足够多的博文去涉及Java流的基础。我只是认为上述这些都是被掩藏起来的宝藏，它们揭示了Java流的潜质。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/410455.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> 2014-02-28 15:25 <a href="http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Gradle构建Java Web应用(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Thu, 23 Jan 2014 13:22:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/409285.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/409285.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/409285.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><span style="font-size: 14pt;">使用Gradle构建Java Web应用</span></div>
<span style="font-size: 10pt;">本文是发布在<a href="http://www.java.net/">java.net</a>上的一篇摘自于&lt;Gradle in Action&gt;一书中的<a href="http://weblogs.java.net/blog/manningpubs/archive/2013/03/16/building-java-web-application-gradle">节选</a>，介绍了使用<a href="http://www.gradle.org/">Gradle</a>构建Java Web应用的过程。刚刚接触Gradle，看到了这篇小文，随手译了出来:-) (2014.01.23最后更新)</span><br />
<br />
<span style="font-size: 10pt;">当今世界，一派繁忙。在职业生涯和私人生活中，我们中间的许多人要同时管理多个项目。你可能常常发现自己处于不知所措及失控的状态。保持规整并专注于价值的关键是一个维护良好的工作清单。当然，你可能总是把你的任务写在一张纸上，但是你也许不可能在你所处的任何地方都可方便地获得这些工作条目？对互联网的访问几乎是无处不在的，无论是通过你的移动电话，还是公共的网络接入点。在&lt;Gradle in Action&gt;一书中，如图1所示的说明性示例是一个很有吸引力的可视化Web应用。</span><br />
<img src="https://www.java.net/sites/default/files/gradle001.jpg" alt="" />
<span style="font-size: 10pt;"><br />
<strong>图1 To Do应用可以通过互联网进行访问，并使用它去管理数据存储中的工作条目</strong></span><br />
<br />
<span style="font-size: 10pt;">Gradle插件表现的如同一个使能器，它会自动地执行这些任务。一个插件通过引入特定领域的规范以及对缺省值敏感的任务去对工程进行扩展。随Gradle发布的插件之一就是Java插件。该Java插件绝不仅仅是提供了源码编译和打包这样的基础功能。它为工程建立了一整套标准的目录布局，它会确保以正确的顺序去执行任务，这样，这些任务在Java工程环境中才是有意义的。现在是时候为我们的应用去创建一个构建脚本并去使用这个Java插件了。</span><br />
<br />
<strong><span style="font-size: 12pt;">构建Java应用</span></strong><br />
<span style="font-size: 10pt;">一开始，每个Gradle工程都会创建一个名为build.gradle的构建脚本。为了创建该脚本，并告诉该工程使用Java插件，应该像这样去做：</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;">apply&nbsp;plugin:&nbsp;'java'</span></div>
<span style="font-size: 10pt;">为了构建你的Java代码，一行代码就够了。但Gradle怎么知道去哪儿找你的源文件呢？该Java插件引入的规范之一就是源代码的路径。默认地，该插件会到目录src/main/java中搜寻产品的源代码。</span><br />
<br />
<strong><span style="font-size: 12pt;">构建Web应用</span></strong><br />
<span style="font-size: 10pt;">通过War插件，Gradle也提供了构建Web应用的扩展支持。War插件扩展了Java插件，它加入了针对Web应用程序开发的规范，并支持归集WAR文件。让我们也在这个工程中用用War插件：</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;">apply&nbsp;plugin:&nbsp;'war'</span></div>
<span style="font-size: 10pt;">Web应用源文件的默认路径是src/main/webapp。假设你已经明确了该应用所必要的Java类。那么要使产品的全部源代码和Web资源文件处于正确路径下，该工程的目录布局应该像下面这样：</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;">.<br />
</span><span style="color: #000000; font-family: Courier;">&#9500;&#9472;&#9472;&nbsp;build.gradle<br />
</span><span style="color: #000000; font-family: Courier;">&#9492;&#9472;&#9472;&nbsp;src<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;main<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;com<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;manning<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;gia<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&#9500;&#9472;&#9472;&nbsp;model<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&#9492;&#9472;&#9472;&nbsp;ToDoItem.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&#9500;&#9472;&#9472;&nbsp;repository<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&#9500;&#9472;&#9472;&nbsp;InMemoryToDoRepository.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&#9492;&#9472;&#9472;&nbsp;ToDoRepository.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&#9492;&#9472;&#9472;&nbsp;web<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoServlet.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#A<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;WEB-INF<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;web.xml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#B<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;css&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#C<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;base.css<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;bg.png<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;jsp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#D<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;&#9500;&#9472;&#9472;&nbsp;index.jsp<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;&#9492;&#9472;&#9472;&nbsp;todo-list.jsp<br />
<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;Web源文件默认目录<br />
</span><span style="color: #000000; font-family: Courier;">#B&nbsp;Web应用描述符文件<br />
</span><span style="color: #000000; font-family: Courier;">#C&nbsp;存储描述如何展现HTML元素的样式单文件的目录<br />
</span><span style="color: #000000; font-family: Courier;">#D&nbsp;存放JSP形式的动态脚本化视图组件</span></div>
<br />
<strong><span style="font-size: 12pt;">声明外部依赖</span></strong><br />
<span style="font-size: 10pt;">在实现这个Web应用的过程，我们使用的一些类，例如javax.servlet.HttpServlet，并非Java标准版(Java SE)的一部分。在构建工程之前，我们需要确保已经声明了这些外部依赖。在Java系统中，依赖类库是以JAR文件的形式去发布和使用的。许多类库可以从仓库，如一个文件系统或中央服务器，中获得。为了使用依赖，Gradle要求你至少定义一个仓库。出于一些考虑，我们将使用公共的可通过互联网进行访问的Maven Central仓库。</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;">repositories&nbsp;{<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;mavenCentral()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #A<br />
</span><span style="color: #000000; font-family: Courier;">}<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;通过http://repo1.maven.org/maven2访问Maven2中央仓库的简短标记</span></div>
<span style="font-size: 10pt;">在Gradle中，依赖是通过配置项来进行分组的。我们将来Servlet依赖使用的配置项是providedCompile。该配置项用于那些在编译时而非运行时所需的依赖。像JSTL这样的运行时依赖，在编译时不会被用到，但在运行时则会被用到。它们都会成为WAR文件的一部分。下面的配置语句块声明了我们应用所需的外部类库：</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;">dependencies&nbsp;{<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;providedCompile&nbsp;'javax.servlet:servlet-api:</span><span style="color: #000000; font-family: Courier;">2.5</span><span style="color: #000000; font-family: Courier;">'<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;runtime&nbsp;'javax.servlet:jstl:</span><span style="color: #000000; font-family: Courier;">1.1.2</span><span style="color: #000000; font-family: Courier;">'<br />
</span><span style="color: #000000; font-family: Courier;">}</span></div>
<br />
<strong><span style="font-size: 12pt;">构建工程</span></strong><br />
<span style="font-size: 10pt;">我们已经准备好构建这个工程了。另到工程中的一个Java插件任务名为build。该任务将编译源代码，运行测试程序并归集WAR文件--所有的这些任务都将以正确的顺序被执行。执行命令gradle build之后，你可能会得到形如下面的输出：</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;gradle&nbsp;build<br />
</span><span style="color: #000000; font-family: Courier;">:compileJava&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #A<br />
</span><span style="color: #000000; font-family: Courier;">:processResources&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:classes<br />
</span><span style="color: #000000; font-family: Courier;">:war&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #B<br />
</span><span style="color: #000000; font-family: Courier;">:assemble<br />
</span><span style="color: #000000; font-family: Courier;">:compileTestJava&nbsp;UP-TO-DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #C<br />
</span><span style="color: #000000; font-family: Courier;">:processTestResources&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:testClasses&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:test&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #D<br />
</span><span style="color: #000000; font-family: Courier;">:check<br />
</span><span style="color: #000000; font-family: Courier;">:build<br />
<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;编译产品的Java源代码<br />
</span><span style="color: #000000; font-family: Courier;">#B&nbsp;War插件提供的任务，用于归集WAR文件<br />
</span><span style="color: #000000; font-family: Courier;">#C&nbsp;编译Java测试源代码<br />
</span><span style="color: #000000; font-family: Courier;">#D&nbsp;运行单元测试</span></div>
<br />
<span style="font-size: 10pt;">上述输出的每一行都代表执行了一个由Java或War插件提供的任务。你可能会注意到，有一些任务被标记为UP-TO-DATE。它的意思是指该任务被跳过去了。Gradle的增量构建支持策略会自动识别不需要执行的工作。特别是在大型商业项目中，该特性会极大地节省时间。</span><br />
<span style="font-size: 10pt;">在该工程的根节目录中，你将会发现一个名为build的子目录，它包含有执行构建之后的全部输出，包括类文件，测试报告，归集的WAR文件，以及像manifest这样的在打包时需要的临时文件。如下就是执行构建工作之后的工程目录结构：</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;">.<br />
</span><span style="color: #000000; font-family: Courier;">&#9500;&#9472;&#9472;&nbsp;build<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;classes<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #A<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;com<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;manning<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;gia<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;model<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoItem.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;repository<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;InMemoryToDoRepository.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoRepository.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&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;&nbsp;web<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#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;&#9500;&#9472;&#9472;&nbsp;ToDoServlet$ToDoListStats.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#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;&#9492;&#9472;&#9472;&nbsp;ToDoServlet.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;dependency-cache<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;libs<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo-webapp.war&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #B<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;reports<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;tests<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;base-style.css<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;css3-pie-</span><span style="color: #000000; font-family: Courier;">1</span><span style="color: #000000; font-family: Courier;">.0beta3.htc<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;index.html<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;report.js<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;style.css<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;test-results<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;binary<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;test<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;results.bin<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;tmp<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;war<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;MANIFEST.MF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #C<br />
</span><span style="color: #000000; font-family: Courier;">&#9500;&#9472;&#9472;&nbsp;build.gradle<br />
</span><span style="color: #000000; font-family: Courier;">&#9492;&#9472;&#9472;&nbsp;src<br />
<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;包含Java类文件的默认目录<br />
</span><span style="color: #000000; font-family: Courier;">#B&nbsp;归集的WAR文件<br />
</span><span style="color: #000000; font-family: Courier;">#C&nbsp;用于WAR的临时manifest文件</span></div>
<span style="font-size: 10pt;">你已经知道如何从一个基于标准目录结构的Web工程去构建WAR文件。现在是时候将它布署到一个Servlet容器中去了。在下一节中，我们将在本地开发机器中启动Jetty去运行这个Web应用。</span><br />
<br />
<strong><span style="font-size: 12pt;">运行应用</span></strong><br />
<span style="font-size: 10pt;">在本地机器中运行一个Web应用应该很容易，能够实践快速应用开发(RAD)，并能够提供快速的启动时间。最棒地是，它不要求你部署一个Web容器运行时环境。Jetty一个流行的轻量级开源Web容器，它支持前面提到的所有特性。在这个Web应用中加入一个HTTP模块，它就变成了一个嵌入式实现。Gradle的Jetty插件扩展了War插件，它提供的任务可以将一个Web应用部署到嵌入式容器中，并能够启动该应用。在你的构建脚本中，可以像如下那样使用这个插件：</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;">apply&nbsp;plugin:&nbsp;'jetty'</span></div>
<span style="font-size: 10pt;">这个将被我们用于启动Web应用的任务名为jettyRun。它甚至可以在无需创建WAR文件的情况下启动一个Jetty容器。执行上述命令后会得到如下形式的输出：</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;gradle&nbsp;jettyRun<br />
</span><span style="color: #000000; font-family: Courier;">:compileJava<br />
</span><span style="color: #000000; font-family: Courier;">:processResources&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:classes<br />
</span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Building&nbsp;&gt;&nbsp;:jettyRun&nbsp;&gt;&nbsp;Running&nbsp;at&nbsp;http://localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; font-family: Courier;">/todo-webapp-jetty</span></div>
<span style="font-size: 10pt;">在上述输出的最后一行中，该插件告诉了你Jetty即将侦听的请求地址。打开一个你喜欢的浏览器，并输入上述地址。最后，我们会看到这个To Do Web应用的行为。图2展示在一个浏览器中查看到该应用界面的截屏。</span><br />
<img src="https://www.java.net/sites/default/files/gradle002.jpg" alt="" />
<br />
<strong><span style="font-size: 10pt;">图2 To Do应用的Web界面及其行为</span></strong><br />
<span style="font-size: 10pt;"><br />在你通过组合键CTRL+C去停止这个应用之前，Gradle会让它一直运行。Jetty如何知道使用哪个端口和上下文环境去运行这个Web应用？再说一遍，这就是规范。Jetty运行Web应用所使用的默认端口就是8080。</span><br />
<br />
<strong><span style="font-size: 12pt;">总结</span></strong><br />
<span style="font-size: 10pt;">只需要较少的努力，你就可以使用Gradle去构建并运行一个Java Web应用。只要你严格遵循标准目录结构，那么你的构建脚本仅需要两行代码。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/409285.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> 2014-01-23 21:22 <a href="http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.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 Concurrent Animated(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 07 Dec 2013 09:45:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/407310.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/407310.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/407310.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java Concurrent Animated</span></strong></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在最新一期的<a href="http://www.oracle.com/technetwork/java/javamagazine/index.html">Java Magazine</a>中有一篇访谈，介绍了一个学习Java并发编程的动画应用<a href="http://sourceforge.net/projects/javaconcurrenta/">Java Concurrent Animated</a>。该应用以十分直观的方式展示了Java并发工具包中的每一个重要组件，降低了学习Java并发编程的难度。(2013.12.07最后更新)</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：有多少人已经试用过了你的Java Concurrent Animated应用？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：该应用是在2009年7月被引入的，从那时算起，已经有了大约20000的下载量。但考虑到已有约一千万的Java开发者，这个下载量才只是开始。按国家区分，下载最多的分别是美国(23%)，印度(14)和中国(7%)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你可以下载一个可以执行的JAR文件，然后仅需双击它就可以运行了。该应用是由菜单驱动的，或者也可以使用向上或向下键在不同的图像和动画之间进行导航。它能运行在诸如Windows，Mac，Linux等等所有的平台上。它要求安装Java SE 6或更高的版本。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：对这个应用最典型的反馈是什么？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：大家告诉我这个工具很好用。许多人确实对此感到兴奋，尤其是那些正试图向团队教授合适并发技术的老师与领导们。Java是最早在核心类库中引入并发的语言之一。在当时，这是一个很强大的特性，但我们很快就发现一个非常优秀的程序员与会写出很糟糕的并发代码。进行恰当的并发编程是一件困难甚至是不可能的事情，但是如何人们能花些时间去理解一些现有的框架，那么在进行并发编码时所产生潜在错误就会变得极少。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 例如，去看看Java内存模型。开发者经常忽视Java内存模型，而像个幸福的傻瓜一样在编码，那么他们的程序会不太正常，因为Java虚拟机(JVM)和服务器可能无法利用到由Java内存模型所提供的优化。由于内核在速度与数量上都有了增长，厂商们期望能够高效地利用到这些内核，然而由于错误的并发管理，本来如期运行的程序却开始遇到了一些零星的错误。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你是说，这个应用会以我们所虚构的方式去使开发者们能够更快且直观地掌握Java并发的原理与实践？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：那是达到这一目的一个有趣的途径。你知道的，Java Concurrent Animated并不是一个Flash动画。它是一组可交互的Java程序，也即，每个动画都是真地在使用它所要演示的并发组件。在屏幕的右边是一个展示代码片断的面板，由于动画的运行，它会动态地高亮显示及恢复正在执行的代码。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 让你给你一个例子，这个例子发生在ReadWriteLock这个动画中。ReadWriteLock用于确保数据的一致性。它允许不受数量限制的线程去获取读锁，并能并发地对这个锁进行操作。但是，写线程在获取这个锁之前只能等待所有的读线程执行结束。一旦一个写线程获得了这个锁，那么其它的读线程或写线程将无法获取它。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 假设一个写线程正在等待正在执行中的读线程去释放这个读锁，但突然一个新的读线程跑过来了。那么谁应该获得这个锁会比较好呢？这个新的读线程应该跑到写线程前面去吗？毕竟，如果其它的读线程已经获得了这个锁，那么新来的读线程为什么要去等一个尚在等待中的写线程呢？而这实际上这正是Java 5所干的事儿。但某次我在Java 6上运行这个动画时，我注意到行为发生了改变。即，随后而来的读线程在获取到这个锁之前可能要等待所有的写线程先释放锁。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我认为这个新的行为是一个BUG，且向并发专家Heinz Kabutz博士提及了此事。博士解释道，这不是一个错误，而一个特性。如果允许新到的读线程跳到正处于等待中的写线程的前面去，这就存在产生线程饥饿条件的高风险。因为，存在一种很大的可能性，可能没有任何写线程能获得这个锁，它们将永远等待着。这就是一个如何使用动画去警示依赖于JVM运行时版本的线程行为的例子。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：以动画教程的形式来展示特殊值，在Java并发编程中有何与众不同吗？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：Miller定律教会我们，我们的大脑在某一时刻能处理的思维的数量是有限的。人类大脑倾向于进行顺序的思维处理，那么即便我们能够克服身体上的束缚，并能够去正确地进行理解，在以后也很难返回至前去重新构造前面的思维处理。可以肯定地是，如果另一个开发者在以后能深入对其进行研究，那么仍然非常难以从原有的思维成果中再次捕捉到认知轨迹。这样的话，脆弱的代码就会很突然地不能正常工作了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 通过使用框架，我们不仅将并发编程委托给了创建和维护该框架的聪明开发者们，而且还为沟通设计时引入了一个词典。所以，我可以说，&#8220;下面的代码会当作CyclicBarrier去执行&#8221;，而人们会明白那是什么意思。通过为java.util.concurrent中的所有组件都引入一个可交互化的动画应用，开发者们点着鼠标就能很方便地将他们所探究的功能进行可视化，使理解这些算法变得真心简单了。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你当时正在研究某些直觉，这些直觉可以帮助更方便地学习并发编程。从开发者的反馈来看，这些直觉看起来是有效的。</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：是的。例如，我前面解释的ReadWriteLock基本功能。读者们可能理解了，也可能没有。现在让我们看看这个与其有关的动画，如图表1所示。</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_01.PNG" height="568" width="872" /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 绿色线程是读线程，最上面的白色线程(带着菱形箭头)是一个写线程，它下面的白色线程是一个新的读线程，该线程在获取锁之前必须要等待所有的读线程与写线程执行完毕。如果你点击按钮并观看这些动画，会比通过浏览繁冗的解释性文字去进行理解要简单得多了。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：Heinz Kabutz评论道，Java被构建成能够一次性做许多事情，而这正与并发完全相关。你的学习系统是如何提高程序员的技能，以便他们能降低并发错误的风险。</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：经常地，当我要努力克服一个并发问题时，我知道解决方案就存在于某个设计模式中，但是哪一个呢？在开发者探寻一个正确解决方案时，Java Concurrent Animated为他们提供了一个所有方案的目录；在激发出正确方案的过程中，它扮演着向导的角色。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：当你管理的团队正在使用Java并发，并且你和你的团队都想更好地去理解Java并发，Java Concurrent Animated有着它的出发点。是什么导致你使用动画呢，能描述下这个过程吗？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：我的培训是针对投资部门的服务器端Java应用，在那里，并发是一个通常都会受到关注的问题。交易员们要求延迟要低，这样可以确保他们在这个需要于一毫秒窗口时间内捕捉交易机会的比赛中不会成为失败者。批量处理也要求快速完成，等等。所以我开始看到那些可怕的只写(write-only)组件，这些组件使人们在并发编程挣扎着。我自己也身处其中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 某天下午，我正坐在机场内，将要前往芝加哥为我的团队做一个关于并发的讲演。我正对讲演的PPT进行最后的处理，那组幻灯片着重演示了每一个重要的组件。为了引导我浏览java.util.concurrent中每个并发组件的状态，我写了一些状态机，它们展示了一些供我参考用的简单文本消息。在之前的生涯中，我曾在一家互联网创业公司中开发交互式的游戏，所以我懂得许多与动画相关的知识。这使我想到可以将PPT替换成一组交互式的动画应用，那会更为直观。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在等飞机的过程中，我写了一个初步的动画引擎，然后在我的状态机中调用了这个引擎。到了第二天早晨，我已经有一个可用的原型程序。多年来，我一直致力于这个框架，并且吸引了其他专家的建议。我传递过一份早期版本给Brian Goetz，令人惊讶的是，他为每个动画程序都给出了建议。我将他的所有建议到吸收到了该框架中。在我的第一次JavaOne讲演中，Kirk Pepperdine加入了进来。他建议为动画应用在真正的PPT中加入描述，以便讲演者能记住正在讨论的内容。随后我加上那些描述，这确实非常有用--不只是对讲演者有用，对于终端用户也很有用。Heinz Kabutz也加入了那场讲演，并建议修改某些动画，以使它们更为直观。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在另一场讲演中，一个很有激情的软件咨询师Oliver Zeigermann指出，很显然缺少了针对ConcurrentHashMap的动画。我问他是否有兴趣贡献这个动画，随后他添加了那个很有价值的动画程序。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你能带着我们过一遍Java并发工具包中的类吗？并能否解释一下这些动画程序是如何使开发者们更易于深入理解这些类？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：好的，但在没有动画程序的情况下确实很难办到。让我们看看CyclicBarrier，它有两个重要的状态，如图2和图3所示，它们展示了一个障碍和四个成员。在图2中，我们可以看到有三个成员已经到了，所以它们被阻止继续前进。图3展示了，一旦第四个成员也到达了，每个成员又可以向前走了。</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_02.PNG" height="569" width="873" /><br /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_03.PNG" height="570" width="872" /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这就形象地诠释了障碍的概念，亦即，在所有成员到达障碍点之前，每个成员必须等待。随着并发组件复杂度的增加--例如Fork/Join的动画，以及那些演示原生的wait和notify机制的动画--使用动画程序的好处就更不肖说了。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：谈谈在创建这些动画程序的过程中所遇到的一些挑战。</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：有一些挑战。开始时，线程被表示成箭头。对于多数并发组件，这种表示法是有效的。后来我们必须提供一个可视化方案，不仅要表示线程，还要表示BlockingQueue中的对象。所以，我不得不引入一个称之为"精灵类型(sprite-type)"的概念，然后我们有了一个箭头型的精灵类型和一个新的"对象"型的精灵类型。后来，ConcurrentHashMap和AtomicInteger又需要新的精灵类型，因为我们试图要对他们的计算与交换行为进行可视化。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 后面又来了Fork/Join，新的挑战就是如何去表现那些完全不同于现有框架所表现的可视化部件。还有一个挑战，即Fork/Join动画需要解决一个实际的问题，但这个动画应该解决一个什么样的问题呢？</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开始时，我让这个动画程序去求Fibonacci数列，但却行不通。我在这个问题上纠结了两天时间，直到我认识到Fibonacci数列(Fn+1=Fn+Fn-1)无法高效地并行化，因为每个值都依赖于它前面的值。所以，无论你如何试图对其实施并行化，它天生就是一个顺序化的计算。所以我换成了另一个问题--查找数组中的最大元素，这样就好了。在这个动画中，你可以很精确地看到如何使用一个随机的数列去解决这个问题(如图4所示)。</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_04.PNG" /><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你都在哪里讲演过这些动画程序？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：在JavaOne中讲演过几次，在其它的许多会议，如奥斯陆中的JavaZone，苏黎世的Jazoon，纽约的QCon，以及许多SIG(特别兴趣组)和JUG(Java用户组)中也都讲演过。我喜欢讲演，我也喜欢周游世界，而Java Concurrent Animated为我提供了一个极好的机会去做这两件事情。它总能获得极高的评价。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java Concurrent Animated的讲演提供了一种意识，并且它们也向出席的开发者们展示了下载这一框架的价值，而且它还展示了，如果你拥有了框架和灵感启迪，学习并发编程会是多么的容易。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/407310.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-07 17:45 <a href="http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.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>Java.next: 下一代JVM语言(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 05 Feb 2013 16:04:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/395164.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/395164.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/395164.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Java.next: 下一代JVM语言</span></strong></div><span style="font-size: 10pt;">本文是ThoughtWorks公司架构师Neal Ford在IBM <a href="http://www.ibm.com/developerworks/">developerWorks</a>系列文章java.next中的<a href="http://www.ibm.com/developerworks/library/j-jn1/index.html">第一篇</a>，其基于Groovy，Scala和Clojure，讲述了多语言编程的重要性，并对静态类型与动态类型，函数式编程与命令式编程进行了比较。(2013.02.06最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在我与Martin Fowler曾经合作呈现的一次主题演讲中，他作出了一个有洞察性的观点：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <em>Java的遗产将是平台，而不是程序设计语言。</em></span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java技术的原始工程师们作出了一个明智的决定，就是将编程语言与运行时环境分开，最终这使得超过200种语言能够运行在Java平台上。这种架构对于该平台的长期活力是至关重要的，因为计算机程序设计语言的寿命一般都是比较短。从2008年开始，由Oracle主办的一年一度的JVM语言峰会为JVM上其它的语言实现与Java平台工程师进行开放式合作提供了机遇。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 欢迎来到<a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=java+next:">Java.next</a>专栏系列，在本系列的文章中，我将讲述三种现代JVM语言--Groovy，Scala和Clojure--它提供了范式，设计选择与舒适因子之间一种有趣的混合。在此我不会花时间去深入介绍每种语言；在它们各自的站点上都有这类深度介绍。但这些语言社区的站点--它们主要目的是为了传布这些语言--都缺乏客观的信息，或者是该语言不适用的例子。在本系列的文章中我将进行独立地比较，以填补上述空白。这些文章将概述Java.next程序设计语言，以及学习它们的好处。</span><br /><br /><strong><span style="font-size: 12pt;">超越Java</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java程序设计语言达到卓越的程度就是，按Bruce Tate在他的Beyond Java一书中的说法，完美风暴：Web应用的兴起，已有Web技术由于种种原因不能适应需求，企业级多层应用开发的兴起，这些因素共同造就了Java的卓越。Tate也指出这场风暴是一系列独一无二的事件，曾经没有其它语言使用相同的途径达到相同的卓越程序。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java语言已经证明其在功能方面的强大灵活性，但它的语法与固有范式则存在着长期已知的局限性。尽管一些承诺过的变化即将引入到该语言中，但Java语法却不能很容易地支持一些重要的未来语法特性，例如函数式编程中的某些特性。但如果你试图去找到一种语言去替代Java，那么你就找错了。</span><br /><br /><strong><span style="font-size: 10pt;">多语言编程</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 多语言编程--在2006年的一篇博客中我使这个术语重焕活力并重新流行起来--是基于这样的一种认识：没有一种编程语言能够解决每个问题。有些语言拥有某些内建的特性，使其能够更好地适应特定的问题。例如，由于Swing十分复杂，开发者们发现很难编写Java中的Swing UI，因为它要求事先声明类型，为UI动作定义烦人的匿名内部类，还有其它的麻烦事儿。使用更适合构建UI的语言，如Groovy中的SwingBuilder工具，去构建Swing应用会美妙得多。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 运行在JVM上的程序设计语言大量增多，这大大激发了多语言编程理念，因为你可以混用编号语言，并可使用最佳匹配的语言，但同时却维护着相同的底层字节码和类库。例如，SwingBuilder并不是要替代Swing；它只是搭建在已有的Swing API之上。当然，在相当长的时间内，开发者们还是将在JVM之外混合使用编程语言--例如，为特定目的而使用SQL和JavaScript--但在JVM的世界内，混合编程将变得更为流行。ThoughtWorks中的许多项目就合用着多种编程语言，而所有由ThoughtWorks Studios开发的工具则都要使用混合语言。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 即便Java仍是你主要的开发语言，学习一下其它语言是如何工作的会让你将它们纳入你的未来战略中。Java仍将是JVM生态系统中的重要组成部分，但最终它更多是作为该平台的汇编语言--或是由于纯粹的性能原因，或是在应对特殊需求时才会用到它。</span><br /><br /><strong><span style="font-size: 10pt;">编程语言的进化</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 当上世纪八十年代我还在大学时，我们使用着一种称作Pecan Pascal的开发环境。它独一无二的特性就是能使相同的Pascal代码既可运行在Apple II上，又可以运行在IBM PC上。Pecan的工程师们为了实现这一目的使用了一种称作"字节码"的神秘之物。开发者们将他们的Pascal代码编译成"字节码"，该"字节码"则运行在为各个平台编写的原生"虚拟机"上。那是一段可怕的经历！最终程序慢的出奇，即便只是一个简单的类赋值。当时的硬件无法应对这一挑战。</span><br /><span style="font-size: 10pt;">Pecan Pascal之后的十年，Sun发布了使用相同架构的Java，它受限也受利于上世纪九十年代的硬件环境。Java还加入了其它的对开发者友好的特性，如自动的垃圾收集。由于曾经使用过像C++之样的语言，现在我再也不想使用没有垃圾收集功能的语言去编码了。我宁愿花时间在更高抽象层次上去思考复杂的业务问题，而不是像内存管理这样的复杂管道问题。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 计算机语言通常没有很长寿命的原因之一就是语言和平台设计的创新速度。由于我们的平台变得更为强大，它们可以处理更多的额外工作。例如，Groovy的内存化(memoization)特性(2010年加入)会缓存函数调用的结果。不需要手工编写缓存代码，那样会引入潜在的缺陷，你仅仅只是需要调用memoize方法而以，如清单1所示：</span><br /><strong><span style="font-size: 10pt;">清单1. 在Groovy中内存化函数</span></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-size: 10pt;">def&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">static</span><span style="color: #000000; font-size: 10pt;">&nbsp;sum&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;{&nbsp;number&nbsp;</span><span style="color: #000000; font-size: 10pt;">-&gt;</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;factorsOf(number).inject(</span><span style="color: #000000; font-size: 10pt;">0</span><span style="color: #000000; font-size: 10pt;">,&nbsp;{i,&nbsp;j&nbsp;</span><span style="color: #000000; font-size: 10pt;">-&gt;</span><span style="color: #000000; font-size: 10pt;">&nbsp;i&nbsp;</span><span style="color: #000000; font-size: 10pt;">+</span><span style="color: #000000; font-size: 10pt;">&nbsp;j})<br /></span><span style="color: #000000; font-size: 10pt;">}<br /></span><span style="color: #000000; font-size: 10pt;">def&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">static</span><span style="color: #000000; font-size: 10pt;">&nbsp;sumOfFactors&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;sum.memoize()</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单1中，sumOfFactors方法返回的结果会被自动缓存。你还可以使用方法memoizeAtLeast()和memoizeAtMost()去定制缓存行为。Clojure也含有内存化特性，在Scala中也有略有实现。像内存化这样存在于下一代编程语言(以及某些Java框架)中的高级特性也将逐渐地进入到Java语言中。Java的下一个版本中将加入高阶函数(higher-order function)，这使得内存化更容易被实现。通过研究下一代Java语言，你就可以先睹Java的未来特性为快了。</span><br /><br /><strong><span style="font-size: 12pt;">Groovy，Scala和Clojure</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Groovy是二十一世纪的Java语法--浓缩咖啡取代了传统咖啡。Groovy的设计目标是更新并消除Java语法中的障碍，同时还要支持Java语言中的主要编程范式。因此，Groovy要"知晓"诸如JavaBean，它会简化对属性的访问。Groovy会以很快的速度纳入新特性，包括函数式编程中的重要特性，这些特性我将在本系列的后续篇章中着重描述。Groovy仍然主要是面向对象的命令式语言。Groovy区别于Java的两个基本不同点：它是动态而非静态的；它是的元编程能力要好得多。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Scala从骨子里就是为了利用JVM而进行设计的，但是它的语法则是完全被重新设计过了。Scala是强静态类型语言--它的类型要求比Java还严格，但造成的麻烦却很少--它支持面向对象和函数式范式，但更偏好于后者。例如，Scala更喜欢val声明，这会生成不可变变量(类似于在Java中将变量声明为final)赋给var，而var将创建更为大家所熟悉的可变变量。通过对这两种范式的深度支持，Scala为你可能想要的(面向对象的命令式编程)与你所应该想要的(函数式编程)之间架起了一座桥梁。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Clojure是最激进的，它的语法是从其它语言中分离出来，被认为是Lisp的方言。Clojure是强动态类型语言(就像Groovy)，它反映了一种义无反顾的设计决策。虽然Clojure允许你与遗留的Java程序进行全面而深度的交互，但是它并不试图构建一座桥梁去连接面向对象范式。例如，Clojure是函数式编程的铁杆，也支持面向对象以允许与该种范式进行互操作。尽管它支持面对对象程序员所习惯的全部特性，如多态--但，是以函数式风格，而非面向对象风格进行实现的。设计Clojure时遵循了一组核心的工程原则，如软件事务内存(Software Transactional Memory)，这是为了迎合新功能而打破了旧有的编程范式。</span><br /><br /><strong><span style="font-size: 12pt;">编程范式</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 除语法之外，这些语言之间的最有趣的不同之处就是类型及其内在的编程范式：函数式或命令式。</span><br /><br /><strong><span style="font-size: 10pt;">静态类型 vs. 动态类型</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 编程语言中的静态类型要求显式的类型声明，例如Java中的int x;声明语句。动态类型语言并不要求在声明时提供类型信息。此处所考虑的语言都是强类型语言，意即程序在赋值之后能够反射出类型。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java的类型系统广受诟病之处就是其静态类型有太多不便，且又没有提供足够的益处。例如，在当前的有限的类型推导出现之前，Java要求开发者在赋值语句两边要重复地声明类型。Scala的类型比Java的更为静态，但在日常使用中所遇到的不便要少得多，因为它大量使用了类型推导。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 初看Groovy，它似乎有一种行为能够衔接静态与动态之间的隔阂。考虑如清单2所示的简单对象集合工厂：</span><br /><strong><span style="font-size: 10pt;">清单2. Groovy集合工厂</span></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: #0000ff; font-size: 10pt;">class</span><span style="color: #000000; font-size: 10pt;">&nbsp;CollectionFactory&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;def&nbsp;List&nbsp;getCollection(description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">if</span><span style="color: #000000; font-size: 10pt;">&nbsp;(description&nbsp;</span><span style="color: #000000; font-size: 10pt;">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Array-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;ArrayList()<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">else</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">if</span><span style="color: #000000; font-size: 10pt;">&nbsp;(description&nbsp;</span><span style="color: #000000; font-size: 10pt;">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;Stack()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单2中的类表现为一个工厂类，基于传入的description参数，该工厂返回List接口的两种实现--ArrayList或Stack--之一。对于Java开发者，上述代码确保了返回值能够符合约定。然后，清单3中的两个单元测试揭示了一种复杂性：</span><br /><strong><span style="font-size: 10pt;">清单3. Groovy中的集合类型测试</span></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-size: 10pt;">@Test<br /></span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;test_search()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;List&nbsp;l&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertTrue&nbsp;l&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">instanceof</span><span style="color: #000000; font-size: 10pt;">&nbsp;java.util.Stack<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;l.push(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertThat&nbsp;l.size(),&nbsp;is(</span><span style="color: #000000; font-size: 10pt;">1</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;def&nbsp;r&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;l.search(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">}<br /><br /></span><span style="color: #000000; font-size: 10pt;">@Test(expected</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">groovy.lang.MissingMethodException.</span><span style="color: #0000ff; font-size: 10pt;">class</span><span style="color: #000000; font-size: 10pt;">)&nbsp;<br /></span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;verify_that_typing_does_not_help()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;List&nbsp;l&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Array-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertTrue&nbsp;l&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">instanceof</span><span style="color: #000000; font-size: 10pt;">&nbsp;java.util.ArrayList<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;l.add(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertThat&nbsp;l.size(),&nbsp;is(</span><span style="color: #000000; font-size: 10pt;">1</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;def&nbsp;r&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;l.search(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单3中的第一个单元测试中，使用前述的工厂类获得一个Stack对象，并验证它是否确实是Stack对象，然后再执行栈操作，例如push()，size()和search()。然而，在第二个单元测试中，我必须声明一个期望的异常MissingMethodException才能确保该测试能够通过。当我获取一个Array-like的集合，并将它赋给List类型的变量时，我能够验证返回的类型确为一个List对象。但是，当我试图调用search()方法时将触发异常，因为ArrayList并不包含search()方法。因此，这种声明无法在编译时确保方法的调用是正确的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 虽然这看起来像是一个缺陷，但这种行为却是恰当的。Groovy中的类型只是确保赋值语句的有效性。例如，在清单3中，如果返回的实例未实现List接口，将会触发一个运行时异常GroovyCastException。鉴于此，可以肯定Groovy能够与Clojure同跻身于强动态类型语言家族。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 然而，Groovy最新的一些变化使得它的静态与动态之间的隔阂变得扫地清。Groovy 2.0加入了注解@TypeChecked，该注解可使你特别地对类或方法决定进行严格的类型检查。清单4例证该注解的使用：</span><br /><strong><span style="font-size: 10pt;">清单4. 使用注解的类型检查</span></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-size: 10pt;">@TypeChecked<br /></span><span style="color: #000000; font-size: 10pt;">@Test&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;type_checking()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;f&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;CollectionFactory()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;l&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;l.add(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;r&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;l.pop()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;assertEquals&nbsp;r,&nbsp;</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单4中，我加入了注解@TypeChecked，它同时对赋值及随后的方法调用进行了验证。例如，清单5中的代码将不能通过编译： </span></div><strong><span style="font-size: 10pt;">清单5. 防止无效方法调用的类型检查</span></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-size: 10pt;">@TypeChecked<br /></span><span style="color: #000000; font-size: 10pt;">@Test&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;invalid_type()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;f&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;CollectionFactory()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;Stack&nbsp;s&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;(Stack)&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;s.add(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;result&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;s.search(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单5中，我必须对集合工厂返回的对象进行强制类型转换，这样才能允许我调用Stack类中的search()方法。但这种方式会产生一些局限性：当使类型静态化之后，Groovy的很多动态特性将无法工作。然而，上述救命证明了Groovy将继续进行改进，以弥合静态性与动态性之间的分歧。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 所有这些语言都有十分强大的元编程功能，所以更为严苛的类型化可以在事后再添加进来。例如，已有多个分支项目将选择性类型(selective type)引入到Clojure中。但一般认为选择性类型是可选的，它不是类型系统的一部分；它只是一个类型验证系统。</span><br /><br /><strong><span style="font-size: 10pt;">命令式 vs. 函数式</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另一个主要的比较维度就是命令式与函数式。命令式编程注重于单步执行的结构，在许多情况下，它是模仿了早期底层硬件的有益结构。函数式编程则注重将函数作为第一等的结构体，以试图将状态传递与可变性降低到最小。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Groovy在很大程度上是受Java的启发，它在根本上仍然是命令式语言。但从一开始，Groovy就加入了许多函数式命令的特性，并且以后还会加入更多的此类特性。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Scala则弥合了这两种编程范式，它同时支持这两种范式。在更偏向(也更鼓励)函数式编程的同时，Scala依然支持面向对象和命令式编程。因此，为了恰当地使用Scala，就要求团队要受到良好的培训，以确保你不会混用和随意地选择编程范式，在多范式编程语言中，这一直都是一个危险。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Clojure是铁杆的函数式编程语言。它也支持面向对象特性，使得它能够很容易地与其它JVM语言进行交互，它并不试图去弥合这两种范式之间的隔阂。相反，Clojure这种义无反顾的决策使它的设计者所考虑的语句成为很好的工程学实践。这些决策具有深远的影响，它使Clojure能够以开创性的方法去解决Java世界中一些挥之不去的问题(如并发)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在学习这些新语言时所要求的许多思想上的转变就是源自于命令式与函数式之间的巨大差别，而这也正是本系列文章所要探索的最有价值的领域之一。</span><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开发者们正生活在一个多语言编程快速发展的世界中，在这种环境中，要求使用多种不同的语言去解决问题。学习高效地利用新语言可以帮助你决定哪种方法是合适的。即便你无法离开Java，它也会逐步地将下一代JVM语言中的特性纳入到Java中；现在看看这些新特性，就会使你在潜移默化之中掌握到未来的Java语言。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在本系列的下一篇文章中，我将开始通过探索Groovy，Scala和Clojure中的共通之处来对它们进行比较。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/395164.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-02-06 00:04 <a href="http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对Java解序列化早作防备(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Thu, 17 Jan 2013 14:39:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/394367.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/394367.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/394367.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">对Java解序列化早作防备</span></strong></div>&nbsp;&nbsp;&nbsp; 本文是<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中的<a href="http://www.ibm.com/developerworks/library/se-lookahead/index.html">一篇文章</a>，介绍了不使用加密与签章技术，如何防止对不可信数据输入的解序列化。(2013.01.18最后更新)<br /><br />&nbsp;&nbsp;&nbsp; Java序列化允许开发者将Java对象保存为二进制格式，以便将该对象持久化到一个文件中或将其在网络中进行传递。远程方法调用(RMI)使用序列化作为客户端与服务器端之间的通信媒介。当服务从客户端接收二进制数据，以及将输入的数据进行解序列化去构造Java实例时，就会产生多种安全问题。本文关注其中一种问题：骇客可能序列化另一个类的实例，并将其传给服务程序。那么服务程序就会解序列化该恶意对象，并很可能将该对象强制转换为服务所期望得到的合法类型，而这将导致异常的发生。然而，该异常对于确保数据安全性则显得太晚了。本文解释了为什么要以及怎样去实现一种安全的序列化。<br /><br /><strong><span style="font-size: 12pt;">脆弱的类</span></strong><br />&nbsp;&nbsp;&nbsp; 你的服务程序不可能反序列化任意类的对象。为什么不能呢？简单的回答是：因为在服务器端的类路径中可能存有被骇客利用的脆弱类。这些类所包含的代码为骇客造就了拒绝服务(DOS)的条件，或者--在极端情况下--会允许骇客注入任意代码。<br />&nbsp;&nbsp;&nbsp; 你可能会相信存在这种攻击的可能性，但考虑到一个典型的服务器端程序的类路径中存在太多的类，不仅包含你自己的代码，还包括Java核心类库，第三方的类库，以及其它的中间件或框架中的类库。另外，在应用程序的生命周期中，类路径可能会被改变，或者为了应对底层运行环境的变化，应用程序的类路径也可能被修改。当试图去利用这样的弱点时，通过传送多个序列化对象，骇客能够将这些操作组合到一块。<br />&nbsp;&nbsp;&nbsp; 我应该强调一下，仅当满足如下条件时，服务才会解序列化一个恶意对象：<br />&nbsp;&nbsp;&nbsp; 1. 恶意对象的类存在于服务器端的类路径中。骇客不可能随便地传递任意类的序列化对象，因为应用服务可能无法加载这个类。<br />&nbsp;&nbsp;&nbsp; 2. 恶意对象的类要么是可序列化的，要么是可外部化的。(即，服务器端的这个类要实现java.io.Serializable或java.io.Externalizable)<br /><br />&nbsp;&nbsp;&nbsp; 另外，通过从序列化流中直接复制数据，在不调用构造器的情况下，解序列化操作就能产生对象树，所以骇客不可能执行序列化对象类的构造器中的Java代码。<br />&nbsp;&nbsp;&nbsp; 但骇客还有其它途径在服务器端去执行代码。无论JVM在何时去解序列化一个对象，都将实现如下三个方法中的一个，都将调用并执行该方法中的代码：<br />&nbsp;&nbsp;&nbsp; 1. 方法readObject()，当标准的序列化机制不适用时，开发者一般就会用到该方法。例如，当需要对transient成员变量进行赋值时。<br />&nbsp;&nbsp;&nbsp; 2. 方法readResolve()，一般用于序列化单例对象。<br />&nbsp;&nbsp;&nbsp; 3. 方法readExternal()，用于外部化对象。<br /><br />&nbsp;&nbsp;&nbsp; 所以，如果在你的类路径中存在着使用上述方法的类，你就必须意识到骇客可能会在远程调用这些方法。此类攻击在过往曾被用于破坏Applet安全沙箱；同样地，相同的攻击技术也可用于服务器端应用。<br />&nbsp;&nbsp;&nbsp; 继续读下去，将会看到如何才能只允许应用服务对其期望的类的对象进行解序列化。<br /><br /><strong><span style="font-size: 12pt;">Java序列化二进制格式</span></strong><br />&nbsp;&nbsp;&nbsp; 一个对象被序列化之后，二进制数据将包含有元数据(指与数据的结构相关的信息，例如类的名称，成员的数量，以及成员的类型)，及对象数据本身。我将以一个简单的Bicycle类作为例子，如清单1所示，该类包含三个成员变量(id，name和nbrWheels)以及与之对应的set与get方法。<br /><strong>清单1. Bicycle类</strong><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: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer;<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;Bicycle&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;java.io.Serializable&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; ">5754104541168320730L</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; ">int</span><span style="color: #000000; ">&nbsp;id;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;String&nbsp;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;nbrWheels;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Bicycle(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;id,&nbsp;String&nbsp;name,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;nbrWheels)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.id&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;id;<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;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.nbrWheels&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nbrWheels;<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;name)&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;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;setId(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;id)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.id&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;id;<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;getId()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;id;<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;getNbrWheels()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;nbrWheels;<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;setNbrWheels(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;nbrWheels)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.nbrWheels&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nbrWheels;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 在一个清单1所示类的实例被序列化之后，其数据流如清单2所示：<br /><strong>清单2. Bicycle类的序列化流</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; ">000000</span><span style="color: #000000; ">:&nbsp;AC&nbsp;ED&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">05</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;2C&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;6F&nbsp;6D&nbsp;2E&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">62</span><span style="color: #000000; ">&nbsp;6D&nbsp;2E&nbsp;|&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;com.ibm.|<br /></span><span style="color: #000000; ">000016</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">62</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;2E&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">67</span><span style="color: #000000; ">&nbsp;2E&nbsp;4C&nbsp;6F&nbsp;6F&nbsp;6B&nbsp;</span><span style="color: #000000; ">41</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">68</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">64</span><span style="color: #000000; ">&nbsp;|ba.scg.LookAhead|<br /></span><span style="color: #000000; ">000032</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">44</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;6C&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;7A&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;2E&nbsp;</span><span style="color: #000000; ">42</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;|Deserializer.Bic|<br /></span><span style="color: #000000; ">000048</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">79</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;6C&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;4F&nbsp;DA&nbsp;AF&nbsp;</span><span style="color: #000000; ">97</span><span style="color: #000000; ">&nbsp;F8&nbsp;CC&nbsp;C0&nbsp;DA&nbsp;</span><span style="color: #000000; ">02</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">03</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">49</span><span style="color: #000000; ">&nbsp;|ycle&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;I|<br /></span><span style="color: #000000; ">000064</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">02</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">64</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">49</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">09</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">62</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">57</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">68</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;6C&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;|&#183;&#183;idI&#183;&#183;nbrWheels|<br /></span><span style="color: #000000; ">000080</span><span style="color: #000000; ">:&nbsp;4C&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">04</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;6D&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">74</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">12</span><span style="color: #000000; ">&nbsp;4C&nbsp;6A&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">76</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;2F&nbsp;|L&#183;&#183;name&#183;&#183;&#183;Ljava/|<br /></span><span style="color: #000000; ">000096</span><span style="color: #000000; ">:&nbsp;6C&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">67</span><span style="color: #000000; ">&nbsp;2F&nbsp;</span><span style="color: #000000; ">53</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">74</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">67</span><span style="color: #000000; ">&nbsp;3B&nbsp;</span><span style="color: #000000; ">78</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">70</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;|lang/String</span><span style="color: #008000; ">;</span><span style="color: #008000; ">&#183;&#183;&#183;&#183;|</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">000112</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">01</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">74</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">08</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">55</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">79</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;6C&nbsp;|&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;Unicycl|<br /></span><span style="color: #000000; ">000128</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">65</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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|e|</span></div><br />&nbsp;&nbsp;&nbsp; 对上述数据应用标准的对象序列化流协议，你将看到如清单3所示的序列化对象：<br /><strong>清单3. 被序列化的Bicycle对象的细节</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; ">STREAM_MAGIC&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0xACED&nbsp;<br />STREAM_VERSION&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">5</span><span style="color: #000000; "><br />newObject<br />&nbsp;&nbsp;&nbsp;&nbsp;TC_OBJECT&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x73<br />&nbsp;&nbsp;&nbsp;&nbsp;newClassDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_CLASSDESC&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x72<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;className<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0x2C&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">44</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">59</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;com.ibm.ba.scg.LookAheadDeserializer.Bicycle<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;serialVersionUID&nbsp;(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0x4FDAAF97F8CCC0DA&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">5754104541168320730</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classDescInfo<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classDescFlags&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x02&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;SC_SERIALIZABLE<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</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;&nbsp;&nbsp;&nbsp;field</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0</span><span style="color: #800000; font-weight: bold; ">]</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;primitiveDesc<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;prim_typecode&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;I&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;integer<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;fieldName<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;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;id<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">1</span><span style="color: #800000; font-weight: bold; ">]</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;primitiveDesc<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;prim_typecode&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;I&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;integer<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;fieldName<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;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">9</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;text&nbsp;(</span><span style="color: #000000; ">9</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;nbrWheels<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">2</span><span style="color: #800000; font-weight: bold; ">]</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;objectDesc<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;obj_typecode&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;L&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;object<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;fieldName<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;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">4</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;text&nbsp;(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;&nbsp;name<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;className1<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;TC_STRING&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x74<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;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0x12&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">18</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;&nbsp;text&nbsp;(</span><span style="color: #000000; ">18</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;Ljava/lang/String</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classAnnotation<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_ENDBLOCKDATA&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x78<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;superClassDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_NULL&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x70<br />&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;id<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">1</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nbrWheels<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">2</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_STRING&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x74<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&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;text&nbsp;(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;Unicycle</span></div><br />&nbsp;&nbsp;&nbsp; 从清单3中你可以看到该序列化对象的类型为com.ibm.ba.scg.LookAheadDeserializer.Bicycle，它的ID为0，只有一个轮子，即它是一个独轮车。 <br />&nbsp;&nbsp;&nbsp; 重点是这个二进制格式包含一种文件头，这就允许你对输入进行校验。<br /><br /><strong style="font-size: 12pt;">类校验</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; 如你在清单3中所看到的，在读取该二进制流时，在对象本身出现之前，首先会看到该序列化对象的类型描述。这种结构就允许实现自己的算法去读取类型描述，并依靠类的名称去决定是否继续读取该序列化流。幸运地是，通过使用Java提供的一个常用于定制类加载的"钩子"，你能很容易地实现该功能--即，覆盖resolveClass()方法。这个"钩子"方法非常适合用于提供定制的校验功能，无论序列化流何时包含了不被期望的类，你都可以用这个方法去抛出一个异常。你需要继承类java.io.ObjectInputStream，并覆盖其中的resolveClass()方法。清单4中的代码就利用该项技术确保只有Bicycle类的实例才可被解序列化。<br /><strong>清单4. 定制校验"钩子"程序</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: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.IOException;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.InputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.InvalidClassException;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectInputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectStreamClass;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer.Bicycle;<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;LookAheadObjectInputStream&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;ObjectInputStream&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;LookAheadObjectInputStream(InputStream&nbsp;inputStream)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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: #0000FF; ">super</span><span style="color: #000000; ">(inputStream);<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;Only&nbsp;deserialize&nbsp;instances&nbsp;of&nbsp;our&nbsp;expected&nbsp;Bicycle&nbsp;class<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;resolveClass(ObjectStreamClass&nbsp;desc)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassNotFoundException&nbsp;{<br />&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; ">desc.getName().equals(Bicycle.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">.getName()))&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;InvalidClassException(<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; ">Unauthorized&nbsp;deserialization&nbsp;attempt</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;desc.getName());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; ">super</span><span style="color: #000000; ">.resolveClass(desc);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 通过对com.ibm.ba.scg.LookAheadDeserializer类的实例调用readObject()方法，就可以防止对不被期望的对象进行解序列化操作。<br />&nbsp;&nbsp;&nbsp; 作为一个示例应用程序，清单5序列化了两个对象--一个是期望的类(com.ibm.ba.scg.LookAheadDeserializer.Bicycle)的实例，另一个是不被期望的类(java.io.File)的实例--然后使用清单4中的定制校验"钩子"程序去尝试它们进行解序列化。<br /><strong>清单5. 使用定制的"钩子"程序</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: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ByteArrayInputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ByteArrayOutputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.File;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.IOException;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectInputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectOutputStream;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer.Bicycle;<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;LookAheadDeserializer&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; ">byte</span><span style="color: #000000; ">[]&nbsp;serialize(Object&nbsp;obj)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayOutputStream&nbsp;baos&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ByteArrayOutputStream();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;oos&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ObjectOutputStream(baos);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oos.writeObject(obj);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;buffer&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;baos.toByteArray();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oos.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baos.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;buffer;<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; ">static</span><span style="color: #000000; ">&nbsp;Object&nbsp;deserialize(</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;buffer)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassNotFoundException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayInputStream&nbsp;bais&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ByteArrayInputStream(buffer);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;We&nbsp;use&nbsp;LookAheadObjectInputStream&nbsp;instead&nbsp;of&nbsp;InputStream</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectInputStream&nbsp;ois&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;LookAheadObjectInputStream(bais);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;obj&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ois.readObject();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ois.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bais.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;obj;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<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; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Serialize&nbsp;a&nbsp;Bicycle&nbsp;instance</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;serializedBicycle&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;serialize(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Bicycle(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Unicycle</span><span style="color: #000000; ">"</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;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Serialize&nbsp;a&nbsp;File&nbsp;instance</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;serializedFile&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;serialize(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Pierre&nbsp;Ernst</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Deserialize&nbsp;the&nbsp;Bicycle&nbsp;instance&nbsp;(legitimate&nbsp;use&nbsp;case)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bicycle&nbsp;bicycle0&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(Bicycle)&nbsp;deserialize(serializedBicycle);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(bicycle0.getName()&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;has&nbsp;been&nbsp;deserialized.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Deserialize&nbsp;the&nbsp;File&nbsp;instance&nbsp;(error&nbsp;case)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bicycle&nbsp;bicycle1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(Bicycle)&nbsp;deserialize(serializedFile);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Exception&nbsp;ex)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(System.err);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />当运行该应用程序时，在试图去java.io.File的对象进行解序列化之前，JVM就抛出异常，如图1所示：<br /><strong>图1. 应用程序输出</strong><br /><img alt="" src="http://www.ibm.com/developerworks/library/se-lookahead/application-output.jpg" height="123" width="580" /><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br />&nbsp;&nbsp;&nbsp; 本文向你展示了在序列化流中发现不被期望的类之后，若不使用加密，签章，或简单的成员变量校验等手段，如何能尽快地停止Java解序列化操作。<br />&nbsp;&nbsp;&nbsp; 需要记住的是，整棵对象树(根对象，及其所有的成员对象)是在解序列化过程中进行组建的。在更为复杂的情况下，你可能必须允许更多的类可被解序列化。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/394367.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-01-17 22:39 <a href="http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索Java语言与JVM中的Lambda表达式(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 01 Jan 2013 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/393674.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/393674.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/393674.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">探索Java语言与JVM中的Lambda表达式</strong></div>&nbsp;&nbsp;&nbsp; Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性，本文是2012年度最后一期<a href="http://www.oracle.com/technetwork/java/javamagazine/index.html?origref=http://java.net/projects/java-magazine">Java Magazine</a>中的<a href="http://www.oraclejavamagazine-digital.com/javamagazine/20121112#pg35">一篇文章</a>，它介绍了Lamdba的设计初衷，应用场景与基本语法。(2013.01.07最后更新)<br /><br />&nbsp;&nbsp;&nbsp; Lambda表达式，这个名字由该项目的专家组选定，描述了一种新的函数式编程结构，这个即将出现在Java SE 8中的新特性正被大家急切地等待着。有时你也会听到人们使用诸如闭包，函数直接量，匿名函数，及SAM(Single Abstract Method)这样的术语。其中一些术语彼此之间会有一些细微的不同，但基本上它们都指代相同的功能。<br />&nbsp;&nbsp;&nbsp; 虽然一开始会觉得Lambda表达式看起来很陌生，但很容易就能掌握它。而且为了编写可完全利用现代多核CPU的应用程序，掌握Lambda表达式是至关重要的。<br />&nbsp;&nbsp;&nbsp; 需要牢记的一个关键概念就是，Lambda表达式是一个很小且能被当作数据进行传递的函数。需要掌握的第二个概念就是，理解集合对象是如何在内部进行遍历的，这种遍历不同于当前已有的外部顺序化遍历。<br />&nbsp;&nbsp;&nbsp; 在本文中，我们将向你展示Lambda表达式背后的动因，应用示例，当然，还有它的语法。<br /><strong><br /><span style="font-size: 12pt;">为什么你需要Lambda表达式</span></strong><br />&nbsp;&nbsp;&nbsp; 程序员需要Lambda表达式的原因主要有三个：<br />&nbsp;&nbsp;&nbsp; 1. 更紧凑的代码<br />&nbsp;&nbsp;&nbsp; 2. 通过提供额外的功能对方法的功能进行修改的能力<br />&nbsp;&nbsp;&nbsp; 3. 更好地支持多核处理<br /><br /><strong>更紧凑的代码</strong><br />&nbsp;&nbsp;&nbsp; Lambda表达式以一种简洁的方式去实现仅有一个方法的Java类。<br />&nbsp;&nbsp;&nbsp; 例如，如果代码中有大量的匿名内部类--诸如用于UI应用中的监听器与处理器实现，以及用于并发应用中的Callable与Runnable实现--在使用了Lambda表达式之后，将使代码变得非常短，且更易于理解。<br /><br /><strong>修改方法的能力</strong><br />&nbsp;&nbsp;&nbsp; 有时，方法不具备我们想要的一些功能。例如，Collection接口中的contains()方法只有当传入的对象确实存在于该集合对象中时才会返回true。但我们无法去干预该方法的功能，比如，若使用不同的大小写方案也可以认为正在查找的字符串存在于这个集合对象中，我们希望此时contains()方法也能返回true。<br />&nbsp;&nbsp;&nbsp; 简单点儿说，我们所期望做的就是"将我们自己的新代码传入"已有的方法中，然后再调用这个传进去的代码。Lambda表达式提供了一种很好的途径来代表这种被传入已有方法且应该还会被回调的代码。<br /><br /><strong>更好地支持多核处理</strong><br />&nbsp;&nbsp;&nbsp; 当今的CPU具备多个内核。这就意味着，多线程程序能够真正地被并行执行，这完全不同于在单核CPU中使用时间共享这种方式。通过在Java中支持函数式编程语法，Lambda表达式能帮助你编写简单的代码去高效地应用这些CPU内核。<br />&nbsp;&nbsp;&nbsp; 例如，你能够并行地操控大集合对象，通过利用并行编程模式，如过滤、映射和化简(后面将会很快接触到这些模式)，就可使用到CPU中所有可用的硬件线程。<br /><br /><strong><span style="font-size: 12pt;">Lambda表达式概览</span></strong><br />&nbsp;&nbsp;&nbsp; 在前面提到的使用不同大小写方案查找字符串的例子中，我们想做的就是把方法toLowerCase()的表示法作为第二个参数传入到contains()方法中，为此需要做如下的工作：<br />&nbsp;&nbsp;&nbsp; 1. 找到一种途径，可将代码片断当作一个值(某种对象)进行处理<br />&nbsp;&nbsp;&nbsp; 2. 找到一种途径，将上述代码片断传递给一个变量<br />&nbsp;&nbsp;&nbsp; 换言之，我们需要将一个程序逻辑包装到某个对象中，并且该对象可以被进行传递。为了说的更具体点儿，让我们来看两个基本的Lambda表达式的例子，它们都是可以被现有的Java代码进行替换的。<br /><br /><strong>过滤</strong><br />&nbsp;&nbsp;&nbsp; 你想传递的代码片断可能就是过滤器，这是一个很好的示例。例如，假设你正在使用(Java SE 7预览版中的)java.io.FileFilter去确定目录是否隶属于给定的路径，如清单1所示，<br /><em>清单1</em><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; ">File&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/an/interesting/location/</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />FileFilter&nbsp;directoryFilter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileFilter()&nbsp;{<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;accept(File&nbsp;file)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;file.isDirectory();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />};<br />File[]&nbsp;directories&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;dir.listFiles(directoryFilter);</span></div><br />&nbsp;&nbsp;&nbsp; 在使用Lambda表达式之后，代码会得到极大的简化，如清单2所示，<br /><em>清单2</em><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; ">File&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/an/interesting/location/</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />FileFilter&nbsp;directoryFilter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(File&nbsp;f)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;f.isDirectory();<br />File[]&nbsp;directories&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;dir.listFiles(directoryFilter);</span></div><br />&nbsp;&nbsp;&nbsp; 赋值表达式的左边会推导出类型(FileFilter)，右边则看起来像FileFilter接口中accept()方法的一个缩小版，该方法会接受一个File对象，在判定f.isDirectory()之后返回一个布尔值。<br />&nbsp;&nbsp;&nbsp; 实际上，由于Lambda表达式利用了类型推导，基于后面的工作原理，我们还可以进一步简化上述代码。编译器知道FileFilter只有唯一的方法accept()，所以它必定是该方法的实现。我们还知，accept()方法只需要一个File类型的参数。因此，f必定是File类型的。如清单3所示，<br /><em>清单3</em><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; ">File&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/an/interesting/location/</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />File[]&nbsp;directories&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;dir.listFiles(f&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;f.isDirectory());</span></div><br />&nbsp;&nbsp;&nbsp; 你可以看到，使用Lambda表达式会大幅降低模板代码的数量。<br />&nbsp;&nbsp;&nbsp; 一旦你习惯于使用Lambda表达式，它会使逻辑流程变得非常易于阅读。在达到这一目的的关键方法之一就是将过滤逻辑置于使用该逻辑的方法的侧边。<br /><br /><strong>事件处理器</strong><br />&nbsp;&nbsp;&nbsp; UI程序是另一个大量使用匿名内部类的领域。让我们将一个点击监听器赋给一个按钮，如清单4所示，<br /><em>清单4</em><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; ">Button&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;Button();<br />button.addActionListener(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ActionListener()&nbsp;{<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;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ui.showSomething();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />});</span></div><br />&nbsp;&nbsp;&nbsp; 这多么代码无非是说"当点击该按钮时，调用该方法"。使用Lambda表达式就可写出如清单5所示的代码，<br /><em>清单5</em><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; ">ActionListener&nbsp;listener&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;event&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{ui.showSomething();};<br />button.addActionListener(listener);</span></div><br />&nbsp;&nbsp;&nbsp; 该监听器在必要时可被复用，但如果它仅需被使用一次，清单6中的代码则考虑了一种很好的方式。<br /><em>清单6</em><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; ">button.addActionListener(event&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{ui.showSomething();});</span></div><br />&nbsp;&nbsp;&nbsp; 在这个例子中，这种使用额外花括号的语法有些古怪，但这是必须的，因为actionPerformed()方法返回的是void，后面我们会看到与此有关的更多内容。<br />&nbsp;&nbsp;&nbsp; 现在让我们转而关注Lambda表达式在编写处理集合对象的新式代码中所扮演的角色，尤其是当针对两种编程风格，外部遍历与内部遍历，之间的转换的时候。<br /><br /><strong><span style="font-size: 12pt;">外部遍历 vs. 内部遍历</span></strong><br />&nbsp;&nbsp;&nbsp; 到目前为止，处理Java集合对象的标准方式是通过外部遍历。之所以称其为外部遍历，是因为要使用集合对象外部的控制流程去遍历集合所包含的元素。这种传统的处理集合的方式为多数Java程序员所熟知，尽管他们并不知道或不使用外部遍历这个术语。<br />&nbsp;&nbsp;&nbsp; 如清单7所示，Java语言为增强的for循环构造了一个外部迭代器，并使用这个迭代器去遍历集合对象，<br /><em>清单7</em><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; ">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;myStrings&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;getMyStrings();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(String&nbsp;myString&nbsp;:&nbsp;myStrings)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(myString.contains(possible))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(myString&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;contains&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000;">&nbsp;possible);<br />}</span></div>&nbsp;&nbsp;&nbsp; 使用这种方法，集合类代表着全部元素的一个"整体"视图，并且该集合对象还能支持对任意元素的随机访问，程序员可能会有这种需求。<br />&nbsp;&nbsp;&nbsp; 基于这种观点，可通过调用iterator()方法去遍历集合对象，该方法将返回集合元素类型的迭代器，该迭代器是针对同一集合对象的更具限制性的视图。它没有为随机访问暴露任何接口；相反，它纯粹是为了顺序地访问集合元素而设计的。这种顺序本性使得当你试图并发地访问集合对象时就会造成臭名昭著的ConcurrentModificationException。<br />&nbsp;&nbsp;&nbsp; 另一种可选的方案就是要求集合对象要能够在内部管理迭代器(或循环)，这种方案就是内部遍历，当使用Lambda表达式时会优先选择内部遍历。<br />&nbsp;&nbsp;&nbsp; 除了新的Lambda表达式语法以外，Lambda项目还包括一个经过大幅升级的集合框架类库。这次升级的目的是为了能更易于编写使用内部遍历的代码，以支持一系列众所周知的函数式编程典范。<br /><br /><strong><span style="font-size: 12pt;">使用Lambda的函数式编程</span></strong><br />&nbsp;&nbsp;&nbsp; 曾经，大多数开发者发现他们需要集合能够执行如下一种或几种操作：<br />&nbsp;&nbsp;&nbsp; 1. 创建一个新的集合对象，但要过滤掉不符合条件的元素。<br />&nbsp;&nbsp;&nbsp; 2. 对集合中的元素逐一进行转化，并使用转化后的集合。<br />&nbsp;&nbsp;&nbsp; 3. 创建集合中所有元素的某个属性的总体值，例如，合计值与平均值。这样的任务(分别称之为过滤，映射和化简)具有共通的要点：它们都需要处理集合中的每个元素。<br />&nbsp;&nbsp;&nbsp; 程序无论是判定某个元素是否存在，或是判断元素是否符合某个条件(过滤)，或是将元素转化成新元素并生成新集合(映射)，或是计算总体值(化简)，关键原理就是"程序必须处理到集合中的每个元素"。<br />&nbsp;&nbsp;&nbsp; 这就暗示我们需要一种简单的途径去表示用于内部遍历的程序。幸运地是，Java SE 8为此类表示法提供了构建语句块。<br /><br /><strong style="font-size: 12pt;">Java SE 8中支持基本函数式编程的类</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; Java SE 8中的一些类意在被用于实现前述的函数式典范，这些类包括Predicate，Mapper和Block--当然，还有其它的一些类--它们都在一个新的java.util.functions包中。<br />&nbsp;&nbsp;&nbsp; 看看Predicate类的更多细节，该类常被用于实现过滤算法；将它作用于一个集合，以返回一个包含有符合谓语条件元素的新集合。何为谓语，有很多种解释。Java SE 8认为谓语是一个依据其变量的值来判定真或假的方法。<br />&nbsp;&nbsp;&nbsp; 再考虑一下我们之前看过的一个例子。给定一个字符串的集合，我们想判定它是否包含有指定的字符串，但希望字符串的比较是大小写不敏感的。<br />&nbsp;&nbsp;&nbsp; 在Java SE 7中，我们将需要使用外部遍历，其代码将如清单8所示，<br /><em>清单8</em><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;printMatchedStrings(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;myStrings)&nbsp;{<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;out&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;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(String&nbsp;s:&nbsp;myStrings)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(s.equalsIgnoreCase(possible))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.add(s);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;log(out);<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 而在即将发布的Java SE 8中，我们使用Predicate以及Collections类中一个新的助手方法(过滤器)就可写出更为紧凑的程序，如清单9所示，<br /><em>清单9</em><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;printMatchedStrings()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;Predicate</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;matched&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;s&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;s.equalsIgnoreCase(possible);<br />&nbsp;&nbsp;&nbsp;&nbsp;log(myStrings.filter(matched));<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 事实上，如果使用更为通用的函数式编程风格，你只需要写一行代码，如清单10所示，<br /><em>清单10</em><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: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printMatchedStrings()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;log(myStrings.filter(s&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;s.equalsIgnoreCase(possible)));<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 如你所见，代码依然非常的易读，并且我们也体会到了使用内部遍历的好处。<br />&nbsp;&nbsp;&nbsp; 最后，让我们讨论一下Lambda表达式语法的更多细节。<br /><br /><strong><span style="font-size: 12pt;">Lambda表达式的语法规则</span></strong><br />&nbsp;&nbsp;&nbsp; Lambda表达式的基本格式是以一个可被接受的参数列表开头，以一些代码(称之为表达式体/body)结尾，并以箭头(-&gt;)将前两者分隔开。<br />&nbsp;&nbsp;&nbsp; 注意：Lambda表达式的语法仍可能会面临改变，但在撰写本文的时候，下面示例中所展示的语法是能够正常工作的。<br />&nbsp;&nbsp;&nbsp; Lambda表达式非常倚重类型推导，与Java的其它语法相比，这显得极其不同寻常。<br />&nbsp;&nbsp;&nbsp; 让我们进一步考虑之前已经看过的一个示例(请见清单11)。如果看看ActionListener的定义，可以发现它只有一个方法(请见清单12)。<br /><em>清单11</em><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; ">ActionListener&nbsp;listener&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;event&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{ui.showSomething();};</span></div><br /><em>清单12</em><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: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;ActionListener&nbsp;{<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;actionPerformed(ActionEvent&nbsp;event);<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 所以，在清单11右侧的Lambda表达式，能够很容易地理解为"这是针对仅声明单个方法的接口的方法定义"。注意，仍然必须要遵守Java静态类型的一般规则；这是使类型推导能正确工作的唯一途径。<br />&nbsp;&nbsp;&nbsp; 据此可以发现，使用Lambda表达式可以将先前所写的匿名内部类代码转换更紧凑的代码。<br />&nbsp;&nbsp;&nbsp; 还需要意识到有另一个怪异的语法。让我们再回顾下上述示例，如清单13所示，<br /><em>清单13</em><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; ">FileFilter&nbsp;directoryFilter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(File&nbsp;f)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;f.isDirectory();</span></div><br />&nbsp;&nbsp;&nbsp; 仅一瞥之，它看起来与ActionListener的示例相似，但让我们看看FileFilter接口的定义(请见清单14)。accept()方法会返回一个布尔值，但并没有一个显式的返回语句。相反，该返回值的类型是从Lambda表达式中推导出来的<br /><em>清单14</em><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: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;FileFilter&nbsp;{<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;accept(File&nbsp;pathname);<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 这就能解释，当方法返回类型为void时，为什么要进行特别处理了。对于这种情形，Lambda表达式会使用一对额外的花括号去包围住代码部分(表达式体/body)。若没有这种怪异的语法，类型推导将无法正常工作--但你要明白，这一语法可能会被改变。<br />&nbsp;&nbsp;&nbsp; Lambda表达式的表达式体可以包含多条语句，对于这种情形，表达式体需要被小括号包围住，但"被推导出的返回类型"这种语法将不启作用，那么返回类型关键字就必不可少。<br />&nbsp;&nbsp;&nbsp; 最后还需要提醒你的是：当前，IDE似乎还不支持Lambda语法，所以当你第一次尝试Lambda表达式时，必须要格外注意javac编译器抛出的任何警告。<br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br />&nbsp;&nbsp;&nbsp; Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性。应用得当，Lambda表达式可使你写出简洁的代码，为已有方法增加额外的功能，并能更好地适应多核处理器。到目前为止，我们能肯定的是，你正急切地想去尝试Lambda表达式，所以咱也别啰嗦了...<br />&nbsp;&nbsp;&nbsp; 你可以从Lambda项目的主页中获得包含有Lambda表达式的Java SE 8快照版。同样地，在试用二进制包时，你也应该先阅读一下"Lambda项目状态"的相关文章，可以在此处找到它们。<br /><br /><span style="color: red;"></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/393674.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-01-01 16:26 <a href="http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>"一致性相等"的陷阱(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Thu, 06 Dec 2012 15:14:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/392569.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/392569.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/392569.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt;">"一致性相等"的陷阱</span></strong></div><span style="font-size: 10pt;">关于Object类中的equals()方法与Comparable接口中的compareTo()方法之间有何种关联，之前还真没考虑过。通过java.net看到<a href="http://blog.joda.org/2012/11/pitfalls-of-consistent-with-equals.html">此文</a>之后，收获了一点儿新知识，希望大家也能如此。(2012.12.09最后更新)</span><br />
<br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">方法equals()与Comparable接口中的compareTo()方法是Java中最基本的两个方法之一，然而它们的定义却围绕着"与相等一致"这一有趣的概念。</span><br />
<span style="font-size: 10pt;"><br />
<strong>equals()方法</strong></span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">Java中的equals()方法既明确，又模糊。Java清楚地定义了如何准确地检验一个equals()方法是可用的。一个恰当的equals()方法必须是自反的，对称的，可传递的，一致的，并能处理null引用。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">然而equals()方法又是不清晰的。Javadoc说到，该方法指定了其它对象是"等于"这个对象的。注意，"等于"是放在引号中的。此处的关键就是，它没有定义如何去判定这种相等性。</span><br />
<ul><li><span style="font-size: 10pt;">对象的一致性(==)默认是继承自Object类</span></li><li><span style="font-size: 10pt;">对象的整体可观测的状态，例如，若两个对象是相等的，那么在应用的其它部分可以用一个对象去替代另一个对象。</span></li><li><span style="font-size: 10pt;">对象信息中的某些部分，如ID，使得检验对象相等性在逻辑上是有意义的。</span></li></ul>

<strong><span style="font-size: 10pt;">compareTo()方法</span></strong><br />&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">Comparable接口定义了可比较性的概念。Javadoc指出compareTo()方法"强制设定了每个实现了该接口的类的对象的全部顺序"。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">实现了Comparable接口的类有一个天然的排序，这可便于存储，也能在不使用单独的Comparator的情况下，用于像TreeSet和TreeMap这样的集合对象。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">该接口的定义明晰，它要求其实现必须确保对称性与传递性，就像equals()方法那样。</span><br />
<br />
<strong><span style="font-size: 10pt;">一致性/非一致性相等</span></strong><br />
<span style="font-size: 10pt;">Comparable接口有如下<a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html">描述</a>：</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">类C的天然排序意味着要与equals()方法保持一致，只有当且仅当e1.compareTo(e2) == 0与e1.equals(e2)有相同的布尔值。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">基本上，这就要求由compareTo()定义的相等性与equals()方法定义的相等性具有相同的概念(除去有null的情况)。乍一看，该要求很简单，但实际上它有其复杂性，后面将会讨论到。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">当考虑到操作符重载时，这种定义就特别有用。若我们假设有一种类Java语言，在这种语言中，==并不表示对象的同一性，而是通过方法去进行比较，大于/小于操作符也是如此，问题是调什么样的方法。在类Java语言中大于/小于天然地就要基于compareTo()方法，而==则要调用equals()方法。</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: #008000; ">//</span><span style="color: #008000; ">&nbsp;our&nbsp;new&nbsp;Java-like&nbsp;language</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(a&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;b)&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Less</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;translation&nbsp;ignoring&nbsp;nulls:&nbsp;if&nbsp;(a.compareTo(b)&nbsp;&lt;&nbsp;0)</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(a&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;b)&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Greater</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;translation&nbsp;ignoring&nbsp;nulls:&nbsp;if&nbsp;(a.compareTo(b)&nbsp;&gt;&nbsp;0)</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(a&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;b)&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Equal</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;translation&nbsp;ignoring&nbsp;nulls:&nbsp;if&nbsp;(a.equals(b))</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Exception(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Impossible&nbsp;assuming&nbsp;no&nbsp;nulls?</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">但如果compareTo()方法不是"一致性相等"，那么上述代码将会抛出异常，因为当a.equals(b)为false时，a.compareTo(b)会返回0。</span><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在集合，如TreeMap，中还会发生其它问题：</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: #008000; ">//</span><span style="color: #008000; ">&nbsp;Foo&nbsp;class&nbsp;is&nbsp;"inconsistent&nbsp;with&nbsp;equals"</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">assert</span><span style="color: #000000; ">&nbsp;foo1.equals(foo2)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">;<br /></span><span style="color: #0000FF; ">assert</span><span style="color: #000000; ">&nbsp;foo1.compareTo(foo2)&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;<br />TreeMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Foo,&nbsp;String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;map&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />map.put(foo1,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />map.put(foo2,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">当使用equals()方法时，这两个对象不相等，但使用compareTo()时，它们却相等。在这种情况下，该Map的元素个数将为1，而非0。</span><br />&nbsp; &nbsp;
<span style="font-size: 10pt;">由于这些"一致性相等"的问题，Javadoc说道"强烈建议(尽管并不要求)天然排序规则要与equals()方法保持一致"。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">JDK中的许多类为了符合"一致性相等"这一规范而实现了Comparable接口。这些类包括Byte，Short，Integer，Long，Character和String。</span><br />
<br />
<span style="font-size: 10pt;">还有些更有趣的类：</span><br />
<span style="font-size: 10pt;">&nbsp; &nbsp; BigDecimal--肯定是"非一致性相等"，比如4.00与4.0不一致，但进行比较时，认为它们是一样的。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Double/Float--该类显式地提供了排序规则，并为正零和负零，以及NaN都提供了相等性检查，以确保它的compareTo()方法符合"一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp; &nbsp; CharSet--该类基于ID或名称。equals()方法对待字段串是大小写敏感的，但compareTo()方法却不这样。虽然名称一般会符合某种标准，但这是一种值得怀疑的"一致性"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; *Buffer(nio)--该簇类的比较基于缓冲存放的内容，在我的测试中equals()和compareTo()是"一致的"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Rdn(ldap)--该类的比较基于状态的标准化格式，因此也是"一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; ObjectStreamField(序列化)--该类的比较基于名称，但会首先对基本数据类型进行排序。因为没有覆盖equals()方法，所以是"非一致性相等"。</span><br />&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">注意：对于大多数的例子，我都不得不查看其源代码或编写测试程序以确定该类是不是符合"一致性相等"。这儿有一个不错地清理Javadoc和检验UUID equals()方法的Adopt-a-JDK任务。</span><br />
<br />
<strong><span style="font-size: 10pt;">JSR-310</span></strong><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">一直看到许多关于BigDecimal的问题，已有计划将<a href="https://github.com/ThreeTen/threeten">JSR-310</a>中的类改造成"一致性相等"，最近的一些<a href="https://github.com/ThreeTen/threeten/issues/132">帖子</a>显示这将造成多么大的争议。</span><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">基本上，为某些类定义equals()和compareTo()看起来很容易。LocalDate表示某单一日历系统中的某个日期，所以它有一个显而易见的排序算法和相等规则。LocalTime则表示某个时刻，所以它也有一个明显的排序算法和相等规则。Instant表示时间线上的某个时刻，那么它的排序与相等也是显见的。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">但在其它的情况下，这就不是那么显而易见了。考虑这样一个类OffsetDateTime：</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; ">dt1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;OffsetDateTime.parse(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">2012-11-05T06:00+01:00</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />dt2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;OffsetDateTime.parse(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">2012-11-05T07:00+02:00</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">这样的两个日期-时刻对象代表时间线上一个相同的时刻点，但它们有不同的本地时，而且相对的UTC/格林威治时间的偏移量也不相同。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">那么就有一个问题要留给读者们...你更倾向于如下哪种观点...</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 1. dt1不等于dt2，compareTo()分别比较本地时与偏移量，使用"一致性相等"(使用独立的Comparator基于时刻对其进行排序)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 2. dt1不等于dt2，compareTo()基于时间线的上时刻点，使用"非一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 3. dt1等于dt2，compareTo()基于时间线的上时刻点，使用"一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 4. dt1等于dt2，且不实现Comparable接口。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 5. dt1不等于dt2，且不实现Comparable接口。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">我个人更倾向于让dt1.equals(dt2)返回true这种方案，但我仍持开放态度。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">顺便地，也可以将这个问题提给BigDecimal，如果你能修改这个类，使其符合"一致性相等"，你会修改它的equals()方法，还是compareTo()方法？</span><br /><br />
</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/392569.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-12-06 23:14 <a href="http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应该使用32位还是64位的JVM？(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 26 Nov 2012 15:41:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/392047.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/392047.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/392047.html</trackback:ping><description><![CDATA[<div>
<div align="center"><strong><span style="font-size: 14pt;">应该使用32位还是64位的JVM？</span></strong></div>
应用使用32位的JVM，还是应该使用64位的JVM，我自己以前还真没想过，大家都想过了吗？本文是<a href="http://plumbr.eu">Plumbr</a>中的一篇<a href="http://plumbr.eu/blog/should-i-use-32-or-64-bit-jvm">博文</a>，为大家提了个醒。(2012.11.27最后更新)<br />
<br />&nbsp;&nbsp;&nbsp; 在我的企业级软件开发职业生涯中，我多次面对这个问题。每隔一段时间我不得不建议配置一个新的特定环境。而往往该问题部分与"我应该使用32位还是64位JVM？"这一问题有关。老实说，一开始我是靠掷硬币来解决的，而不是给出一个合理的答案。(抱歉，兄弟们！)但现在我对这个问题有了更多的领悟，并想与你们分享。<br />
&nbsp;&nbsp;&nbsp; 第一步--越多越好，对吗？即如此，因为64&gt;32，所以答案很简单：如果可能的话，应该总是选择64位？好吧，请耐心点儿。64位架构的坏处是，相同的数据结构会消耗更多的内存，甚至是多很多。我们的测评显示，根据不同的JVM版本和操作系统版本，以及相应的硬件架构，最终会用掉比32位架构多30-50%的堆内存。更大的堆也会造成更长的GC暂停，这会对应用程序造成潜在的影响--在4.5G的堆上执行完全GC肯定会比在3G的堆上执行花费更长的时间。所以，仅仅是因为64比32大就去赶64位JVM的时髦，肯定是错误的。<br />
&nbsp;&nbsp;&nbsp; 但，什么时候才是使用64位JVM的好时机呢？多数情况下，要看堆的大小。在不同的平台下，你很快就得面对32位JVM堆内存的上限问题。下表列出了在不同平台下的这种限制：<br />
Linux&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2GB &nbsp;&nbsp; &nbsp;&nbsp; 特定内核，如hugemem，可以达到3G<br />
Windows&nbsp;&nbsp;&nbsp; 1.5GB &nbsp;&nbsp; 使用"/3G"的启动参数并使用/LARGEADDRESSAWARE参数去编译JRE，则可提高到3G<br />
Mac OS X&nbsp;&nbsp; 3.8GB &nbsp;&nbsp; 警告--未能找到老的Mac，所以没有对其进行测试<br />
<br />
&nbsp;&nbsp;&nbsp; 那么，这有什么坏处？我打赌，你肯定见过在16G的RAM上运行32位的机器。问题就在于，在只有16G RAM的Windows系统中，JVM只能分配到少于10%的内存。<br />
&nbsp;&nbsp;&nbsp; 主因--地址空间。在32位系统中，理论上可以为每个进程分配4G内存。而Windows系统对地址空间的处理使这一理论值无法达到。Windows将进程的地址空间砍掉了一半。一半留给了内核(用户进程无法使用它)，另一半则留给了用户。无论系统中有多大的RAM，32位进程只能使用到2G的RAM。更糟的是--地址空间必须是连续的，所以在实践中，Windows系统最多只为你剩下了1.5-1.8G的堆内存。<br />
&nbsp;&nbsp;&nbsp; 有一个在32位Windows系统中减少内核空间并增加用户空间的窍门，即，可以在boot.ini系统使用/3GB参数。然而，为了能确保它有效，必须使用/LARGEADDRESSAWARE开关去编译或链接JVM。<br />
&nbsp;&nbsp;&nbsp; 不幸地是，至少对于HotSpot JVM无法做到这一点。直到最新的JDK 1.7版本，HotSpot JVM仍未使用该选项进行编译。但又幸运地是，如果你运行2006年之后的JRockit版本，就能享用到2.8-2.9G的堆大小。<br />
&nbsp;&nbsp;&nbsp; 那么，我们是否可以得出结论，如果你的应用要求大于2-3G的内存，你就总是应该运行64位的JVM？也许。但你也必须要清楚应用的场景。我们已经介绍了使用64位JVM的坏处--增加的堆消耗，以及更长的GC中断。让我们分析一下原因。<br />
&nbsp;&nbsp;&nbsp; <strong>问题1：64位JVM需要多出30-50%的内存。</strong>为什么会如此呢？主要是因为内存是以64位架构进行部局。首先，在64位JVM中，对象头有12字节。其次，对象引用会占用4字节或8字节，实际值取决于JVM的参数与堆的大小。相较于32位JVM的8字节对象头和4字节对象引用，毫无疑问会增加一些开销。在我们<a href="http://plumbr.eu/blog/how-much-memory-do-i-need-part-2-what-is-shallow-heap">之前发布的博客</a>中你会找到更多关于如何计算对象内存的相关信息。<br />
&nbsp;&nbsp;&nbsp; <strong>问题2：更长的垃圾收集中断。</strong>构建更大的堆意味着GC要做更多的工作去清理无用的对象。这意味着，在实际应用中构建大于12-16G的堆时，你必须要特别小心。没有很好的性能调优与测评，你很容易就会引起一个耗时数分钟的完全GC。在应用程序的非关键潜在危险场景中，通过对吞吐量的优化或许能解决这一问题，但在多数情况下，它会造成程序中断。<br />
&nbsp;&nbsp;&nbsp; 当我需要更大的内存且又不希望引入由64位架构所造成的开销，那该怎么办呢？在我们<a href="http://plumbr.eu/blog/increasing-heap-size-beware-of-the-cobra-effect">以前的一篇博文</a>中已经涉及了这个问题--通过对堆的分区，GC调优，构建不同的JVM，或对堆分配不同的内存，就可以避免这一问题。<br />
&nbsp;&nbsp;&nbsp; 最后，让我们重申，你应该总是要意识到选择64位JVM的后果，但也不要惧怕这一选择。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/392047.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-11-26 23:41 <a href="http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Custom Layout Manager: PyramidLayout(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/07/15/383156.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sun, 15 Jul 2012 14:14:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/07/15/383156.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/383156.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/07/15/383156.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/383156.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/383156.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt; font-family: Courier;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt; font-family: Courier;">Custom Layout Manager: PyramidLayout</span></strong></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt; font-family: Courier;">已有太多关于自定义部局管理器的文章了。本文仅是一篇学习笔记，描述了如何实现一种像堆金字塔似的部局管理器，很简单，也有点儿意思，可能你也会感兴趣的。(2012.07.17最后更新)</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; </span><br />
<br />
<span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; I have developed Swing application for several years, although I'm not professional GUI developer, I'm shamed of never creating any custom layout manager. Maybe the existing Swing layout managers are too powerful to create new ones. At least, <a href="http://docs.oracle.com/javase/7/docs/api/java/awt/GridBagLayout.html"><span style="font-family: Courier;">GridBagLayout</span></a></span><span style="font-size: 10pt; font-family: Courier;"> is powerful enough for my real works. And we have much more flexible <a href="http://docs.oracle.com/javase/7/docs/api/javax/swing/GroupLayout.html"><span style="font-family: Courier;">GroupLayout</span></a></span><span style="font-size: 10pt; font-family: Courier;"> and <a href="http://docs.oracle.com/javase/7/docs/api/javax/swing/SpringLayout.html"><span style="font-family: Courier;">SpringLayout</span></a></span><span style="font-size: 10pt; font-family: Courier;">, of course, both of them are too complex, in fact I never use them directly. However I indirectly take advantage of GroupLayout due to using <a href="http://netbeans.org/"><span style="font-family: Courier;">NetBeans</span></a></span><span style="font-size: 10pt; font-family: Courier;">' GUI designer <a href="http://netbeans.org/features/java/swing.html"><span style="font-family: Courier;">Matisse</span></a></span><span style="font-size: 10pt; font-family: Courier;">.</span><br />
<br />
<strong><span style="font-size: 12pt; font-family: Courier;">1. Layout Manager basics</span></strong><br />
<span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; Let's start with some layout manager foundation. Before this time I learn to customize layout, I always think layout manager is very mysterious. Layout is like a magic player that put a variety of components to right positions in containers. I haven't browsed any code of any layout, event the simplest one. That's why I think layout is mystery. But it's simple for me now.</span><br />
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt; font-family: Courier;">Generally, all of layout implements one or both of <a href="http://docs.oracle.com/javase/7/docs/api/java/awt/LayoutManager.html"><span style="font-family: Courier;">LayoutManager</span></a></span><span style="font-size: 10pt; font-family: Courier;"> and <a href="http://docs.oracle.com/javase/7/docs/api/java/awt/LayoutManager2.html"><span style="font-family: Courier;">LayoutManager2</span></a></span><span style="font-size: 10pt; font-family: Courier;"> interfaces. LayoutManager2 is LayoutManager's sub-interface, then if someone implements LayoutManager2 that means it really implements LayoutManager. Mostly all modern layouts implements LayoutManager2.</span><br />
<span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; Interface LayoutManager defines the basic methods must be implemented by every layout, all of them are intuitional: add new component--addLayoutComponent(); remove component--removeLayoutComponent(); calculate preferred size--preferredLayoutSize(); calculate minimum size--minimumLayoutSize(); how to layout the components--layoutContainer(). Absolutely, the layoutContainer() method is essential, you must instruct the parent container how to allocate space(bounds) for every component.</span><br />
<span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; The extension interface LayoutManager2 introduces more methods that if you have to: support constraints--addLayoutComponent(Component, Object); specify maximum size--maximumLayoutSize(); specify alignment--getLayoutAlignmentX() and getLayoutAlignmentY(); destroy specific caches or reset some variables when invaliding container--invalidateLayout().</span><br />
<br />
<strong><span style="font-size: 12pt; font-family: Courier;">2. PyramidLayout</span></strong><br />
<span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; Now let's feature a simple and funny layout manager--PyramidLayout. The layout allows container to add components like building a Pyramid, as shown as the image below,</span><br />
<span style="font-size: 10pt;"></span><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/pyramidLayout.PNG" /><br />
<span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; As the above, PyramidLayout puts the first component on the bottom, then puts the second on top of the first, but its bounds is smaller, ... It looks like a Pyramid, doesn't it? Here is the full codes,</span><br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">class</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;PyramidLayout&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">implements</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;LayoutManager2&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">private</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;List</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&lt;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">Component</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&gt;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;comps&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;LinkedList</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&lt;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">Component</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&gt;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">void</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;addLayoutComponent(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Component&nbsp;comp,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Object&nbsp;constraints)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">synchronized</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(comp.getTreeLock())&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comps.add(comp);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">void</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;addLayoutComponent(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;String&nbsp;name,&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Component&nbsp;comp)&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addLayoutComponent(comp,&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">null</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">void</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;removeLayoutComponent(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Component&nbsp;comp)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">synchronized</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(comp.getTreeLock())&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comps.remove(comp);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">float</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;getLayoutAlignmentX(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;target)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0.5F</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">;<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">float</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;getLayoutAlignmentY(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;target)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0.5F</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">;<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">void</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;invalidateLayout(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;target)&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println();<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension&nbsp;preferredLayoutSize(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;parent)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">synchronized</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(parent.getTreeLock())&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insets&nbsp;insets&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;parent.getInsets();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.left&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.right;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.top&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.bottom;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">if</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(comps.size()&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension(width,&nbsp;height);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dimension&nbsp;size&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;comps.get(</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">).getPreferredSize();<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;size.width;<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;size.height;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension(width,&nbsp;height);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension&nbsp;minimumLayoutSize(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;parent)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">synchronized</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(parent.getTreeLock())&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Insets&nbsp;insets&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;parent.getInsets();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.left&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.right;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.top&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;insets.bottom;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">if</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(comps.size()&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension(width,&nbsp;height);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dimension&nbsp;size&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;comps.get(</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">).getMinimumSize();<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;size.width;<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">+=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;size.height;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension(width,&nbsp;height);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension&nbsp;maximumLayoutSize(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;target)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension(Integer.MAX_VALUE,&nbsp;Integer.MAX_VALUE);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">void</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;layoutContainer(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">final</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Container&nbsp;parent)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">synchronized</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(parent.getTreeLock())&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dimension&nbsp;parentSize&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;parent.getSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;compsCount&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;comps.size();<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dimension&nbsp;step&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Dimension(parentSize.width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">/</span><span style="color: #000000; "> </span><span style="color: #000000; font-size: 10pt; font-family: Courier;">(2</span><span style="color: #000000; font-family: Courier;"> *</span><span style="color: #000000; font-size: 10pt;"></span><span style="color: #000000; font-size: 10pt; font-family: Courier;"> compsCount),<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parentSize.height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">/</span><span style="color: #000000; "> </span><span style="color: #000000; font-size: 10pt; font-family: Courier;">(2</span><span style="color: #000000; "> </span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; font-size: 10pt; font-family: Courier;"> compsCount));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">for</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;(</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;i&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">0</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">;&nbsp;i&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&lt;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;compsCount;&nbsp;i</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">++</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">)&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Component&nbsp;comp&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;comps.get(i);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comp.setBounds(calcBounds(parentSize,&nbsp;step,&nbsp;i));<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent.setComponentZOrder(comp,&nbsp;compsCount&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">-</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;i&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">-</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">1</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">private</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Rectangle&nbsp;calcBounds(Dimension&nbsp;parentSize,&nbsp;Dimension&nbsp;step,&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;index)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;x&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;step.width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;index;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;y&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;step.height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;index;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;parentSize.width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">-</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;step.width&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;index;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">int</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">=</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;parentSize.height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">-</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;step.height&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">*</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;index;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Courier;">new</span><span style="color: #000000; font-size: 10pt; font-family: Courier;">&nbsp;Rectangle(x,&nbsp;y,&nbsp;width,&nbsp;height);<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt; font-family: Courier;">
}</span></div><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; Collection instance "comps" manages all of components, in this case, I take a LinkedList object to add and remove UI components. The layout doesn't concern any constraint, so the two addLayoutComponent() methods have the same actions. Please see the codes for details.<br /></span><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; As aforementioned, layoutContainer() method really takes charge of layouting the components. The key work is allocating space for each component, namely, specifying the bounds. Calculating bounds values just applies the simplest arithmetic operations.<br /></span><span style="font-size: 10pt; font-family: Courier;">&nbsp;&nbsp;&nbsp; According to the intention, the bottom component fills the whole parent container, so it determines the preferred and the minimum sizes. For details, please take a look at methods preferredLayoutSize() and minimumLayoutSize(). Since the layout manager doesn't take care of the maximum size, the maximumLayoutSize() method simply returns a constant value.<br /></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/383156.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-07-15 22:14 <a href="http://www.blogjava.net/jiangshachina/archive/2012/07/15/383156.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>推动Java前进--Java Strategy Keynote JavaOne 2011(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Fri, 07 Oct 2011 03:51:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/360101.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/360101.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/360101.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">推动Java前进--Java Strategy Keynote JavaOne 2011</strong></div>每年的JavaOne都会发布关于Java未来发展的计划或愿景，在今年的Java Strategy Keynote中，Oracle则向大家描述了将如何推动Java继续前进。<a href="http://blogs.oracle.com/javaone/entry/moving_java_forward_java_strategy">本文</a>是JavaOne2011的官方博文，概述了Java Strategy Keynote中提及的重要内容。(2011.10.07最后更新)<br /><br />&nbsp;&nbsp;&nbsp; 周四的战略主旨演讲展示了Oracle在对待Java投资与创新方面的长期愿景。该议题包罗广泛，涉及技术，合作伙伴，宣言和路线图--从移动与手持设备，到桌面应用，再到云。<br />&nbsp;&nbsp;&nbsp; 早上，先由David Ward开始，他是Juniper Networks平台系统部门的CTO与首席架构师。Ward详述了在云时代开发者们认知并利用可编程的"由软件定义的网络"时代的必要性。Ward探究了直接对网络进行编程的应用接口，告之了它最得意的功能，使用网络设备确定实际的位置/拓扑，终端设备的功能，实时应用的要求，以及其它方面。总之，这些接口定义了一个双向的交互与可编程特性。Ward说道，"来源于网络的是对其拓扑的实时理解，而从应用空间到网络中去的是将通信引导到高效途径中"。<br /><br /><strong><span style="font-size: 12pt;">推动Java前进：三大支柱</span></strong><br />&nbsp;&nbsp;&nbsp; 随后，Hasan Rizvi，Oracle Fusion中间件与Java部门的高级副总裁，简要探讨了"推动Java前进"的三大支柱：正确的技术，Java社区，以及Oracle对Java的领导/管理。在该环节中，Rizvi提供了一个近期里程碑的记录，包括在今年夏天发布的JDK 7，正在开发中的(且由社区深度参与的)Java EE 7，JavaFX 2.0，以及刚刚针对Mac OS X发布的JDK 7预览版。他也提到了IBM，Apple以及SAP成为OpenJDK社区的成员，当然还有其它成员。<br />&nbsp;&nbsp;&nbsp; 然后，Intel的Java技术主管Jason Gartner，RedHat的高级工程主管Mark Little博士，以及Intel的软件工程主管Steven Chin也走上台，加入到Rizvi的演讲中。Gartner宣布，上周发布了<a href="http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html">IBM平台上的Java 7</a>，他也提到，尽管Oracle与IBM之间存在竞争关系，但Java平台基于标准的合作本质是它最强大的力量之一。他还稍稍讨论了Java EE 6的开放式开发成果，以及这对JBoss产品的益处。Chin讨论了Oracle与Intel之间的合作，在过去4年中，这一合作致使Java的性能在Intel硬件中提高了14倍。<br /><br /><strong><span style="font-size: 12pt;">延伸Java的触角</span></strong><br />&nbsp;&nbsp;&nbsp; Adam Messinger，负责Java SE与Java ME产品线的副总裁，讨论了延伸Java的触角，以跨越新的应用模型及新的设备类型的目的。一种方法就是通过模块化，这是Java SE 8中的计划。同时，Java 7的Coin项目与Java 8的Lambda项目致力于更高的开发者生产效率。如下的路线图有清晰的描述：<br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2011/slide_15_large_formatted.gif" height="278" width="643" /><br /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2011/slide_16_large_formatted.gif" height="278" width="643" /><br /><br />&nbsp;&nbsp;&nbsp; Rob Benso，Twitter的运行时系统主管，随后也与Messinger同台演讲。Benson详述了Twitter在需要一个成熟且有高伸缩能力的技术时，是如何应用Java的。"我们每天要处理大约2.3亿条推特，我们的流API每天要推送大约6TB的数据，并且我们的公共API每天要服务大约13亿个请求。所以我们期望中的运行时环境要能够处理当前及未来的通信量。"Twitter选择了Java，是由于它有一个庞大且充满活力的开源社区。Twitter当场宣布，他们已经加入了OpenJDK，同样也加入了JCP。<br />&nbsp;&nbsp;&nbsp; Messinger重申推进JavaFX在战略上的重要性，JavaFX是Oracle首要的RIA开发环境，它包含对已有Swing应用的交互，以及能进行可视化开发的JavaFX Scene Builder工具。随后，Messinger的同事Nandini Ramani提供了一个实验性的JavaFX游戏的原型，该原型能运行在多种便携设备上--包括Windows，Linux，甚至是Apple iPad。<br />&nbsp;&nbsp;&nbsp; Messinger还宣布了Oracle对JavaFX的开源计划，首先开源的是组件，然后是框架的其它部分。只要OpenJDK社区提出要求，这一开源计划就会执行。另外，他们还计划对JavaFX进行标准化，使它成为Java SE的一个标准组成，这可能要到Java 8之后了。<br /><br /><strong style="font-size: 12pt;">Java移动与嵌入式的三大目标</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; 后来，Messinger谈到Oracle针对Java移动与嵌入式体系的三大目标--在Java SE设备与Java ME设备之间起到桥梁作用(同步CLDC与JDK的版本/聚合CDC与嵌入式Java SE应用)，完整地涵盖嵌入式垂直市场，并在运行时环境与工具方面提供深度的内容与服务整合。这必将导致Java Card技术应用于极小设备，Java ME应用于小的嵌入式环境，而Java SE应用于更大的嵌入式市场。<br />&nbsp;&nbsp;&nbsp; 在真正的嵌入式领域中，ARM的执行副总裁Lance Howarth探讨了"无处不在的计算"的将来。作为制造商随处都使用的32位RISC微处理器(基于ARM的设备已经出厂了610万台)，Howarth预测对于智能计算设备，我们很少意识到，也很少见到把Java作为实现这一设想的关键。对之后，移动手机应用商店GetJar的CEO Ilya Lars探讨他们对移动领域的愿景，以及他们对Java开放标准的提案。<br /><br /><strong style="font-size: 12pt;">Java EE路线图</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; Oracle开发部门的副总裁Cameron Purdy详述了Java EE路线图。Purdy提到Java EE是"企业级应用所依赖的唯一标准开发平台"，这些应用可以跨越不同的社区，不同的软件提供商，以及不同的开源实现。他还讲到Java EE当前是企业级开发者的首选，它已经被下载4000万次了。Java EE的目标是使EE易于开发--在从事往常复杂而费解的任务时，EE会使Java开发者们感到更为自然，同时为下一代企业应用建立了一个开放的，由社区驱动的，且基于标准的平台。他探讨了很多Java EE 7的独创特性，如多租用(multi-tenancy)，按需扩容(capacity on demand)，以及自动提供(auto-provisioning)，这些特性使高级云应用及其平台成为一种服务。<br />&nbsp;&nbsp;&nbsp; 随后，ESPN的Sean Comerford与Purdy讨论了在ESPN.com在当前的企业需求中对Java EE的选择。"Glassfish给了我们相同的性能，但与其它可选技术相比，它还具备高得多的可伸缩性。在处理每秒3000次请求的场景中，对我们的产品部署进行了测试。"对于ESPN，游戏的规则就是可伸缩性，还伴随着未来在任何地方、任何设备上提供服务的潜在可能。<br />&nbsp;&nbsp;&nbsp; 然后，Adam Messinger以对Avatar项目的介绍结束了本场会议。Avatar项目是Oracle针对动态富客户端的混合编程模型，它在浏览器中集成HTML 5作为UI，使Java应用作为控制器与模型，而Java EE 7则在后台的云中--这统一了Java ME，Java SE和Java EE。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/360101.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-10-07 11:51 <a href="http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Duke's Choice Award 2011(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 04 Oct 2011 16:13:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/360024.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/360024.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/360024.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">Duke's Choice Award 2011</strong></div>JavaOne 2011还在进行中，前日(2011.10.03)，本年度的Duke's Choice Award已新鲜出炉。<a href="http://blogs.oracle.com/javaone/entry/and_the_winners_are_the">本文</a>是JavaOne的官方博客，记录了本次全部获奖应用，大家可以看看这些应用有何高明之处。(2011.10.05最后更新)<br /><br />10月2号，星期日的晚上，在JavaOne Open House，Oracle欢迎并祝贺了2011年度Duke's Choice Award的获得者们。这些睿智的开发者们证明了由Java应用所创造的极致创新能力。<br />获奖者是...<br />1. <a href="http://community.jboss.org/wiki/Arquillian">Arquillian</a>--该项目使基于Java的应用的集成测试得到了简化。它为开发者们提供了一种在远程或内嵌的容器中，或与容器交互的客户端中，测试应用程序的简单机制。<br /><br />2. dooApp--<a href="http://www.dooapp.com/en">Infiltrea</a>是一个使用JavaFX与Java SE平台构建的点对点解决方案，该方案是为环保建筑工程师而设计的，他们的工作是测量建筑物的空气密度。<br /><br />3. <a href="http://www.inductiveautomation.com/">Inductive Automation</a>--Ignition是一个基于Java的Web应用，它包含一个商业记分卡，该卡使用一个中央Web服务器将不同的生产机器集成到一起。该系统涵盖了人机界面(HMI)，管理控制与数据采集(SCADA)，以及生成执行系统(MES)，这使得能够快速地进行项目开发与部署。<br /><br />4. <a href="http://download.oracle.com/javaee/6/firstcup/doc/gcrky.html">jHome</a>--一个用于Glassfish/Java EE的开源的完整家用自动化API，它使开发者能够在家中控制任何器具，例如电灯，LED灯和咖啡机。<br /><br />5. JFrog--它的<a href="http://www.jfrog.com/products.php">Artifactory</a>这是世界上第一个双仓库管理器。它基于Java内容仓库(JCR)API规范构建而成，能够帮助开发者改变通过传递高并发和不匹配的数据完成性来构建并管理软件模块的途径。<br /><br />6. <a href="http://www.zeroturnaround.com/jrebel/">JRebel</a>--这是一个JVM插件，它使得Java开发者能够即时地查看应用中哪些程序发生了改变。它允许开发者跳过构建与重布署阶段，它已经阻止了3900万次重部署了。<br /><br />7. LMAX--<a href="http://code.google.com/p/disruptor/">LMAX Disruptor</a>是一个多线程，开源的高并发编程框架，它是为高性能且低延迟事务处理而设计的。作为LMAX的Java交易系统，LMAX Disruptor替代了java.util.concurrent.ArrayBlockingQueue，而且比它快80倍以上。<br /><br />8. <a href="http://www.rockwellautomation.com/rockwellsoftware/">Rockwell Automation</a>--这是一个开创了下一代，基于Java的HMI设备产品线，该设置将允许自动地对工厂生产线进行数据通信与交换。<br /><br />9. <a href="http://sodbeans.sourceforge.net/">Sodbeans</a>--该项目是一个开源的，基于NetBeans的模块套件，它是为在现代编程环境中改善盲人与外界交流的能力而设计的。这个NetBeans模块套件包含一个新的易用的编程语言，一个使NetBeans兼容多平台屏幕阅读器的文本语音转换引擎，一组为让盲人易于编程而设计的语言特性。<br /><br />10. <a href="http://www.jboss.org/netty">Netty</a>--该项目是一个基于Java的NIO C/S框架，它使得能够快速且方便地进行网络程序的开发。它极大地简化了网络编程，例如TCP和UDP的套接字服务器。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/360024.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-10-05 00:13 <a href="http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IBM Java SDK 7(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 04 Oct 2011 08:34:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/360010.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/360010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/360010.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">IBM Java SDK 7<br /></strong></div>昨天(2011.10.03)，IBM发布了它的Java SDK 7，目前仅支持AIX和Linux平台。<a href="http://www-01.ibm.com/support/docview.wss?uid=swg21516002">本文</a>是IBM上的一篇新闻，大家可以看看它有哪些新特性或变化。(2011.10.04最后更新)<br /><br />IBM Java SDK 7现在已经可用了，目前支持AIX和Linux平台。<br />在developerWorks中下载SDK，请见<a href="http://www.ibm.com/developerworks/java/jdk/index.html">http://www.ibm.com/developerworks/java/jdk/index.html</a>。<br /><br /><strong style="font-size: 12pt;">新特性</strong><br style="font-size: 12pt;" />该版本含有许多新的改进与特性，包括：<br />1. Coin项目(JSR 334)的语言改进特性，例如：支持字符串的switch语句块，钻石操作符，以及自动的资源管理。<br />2. NIO 2(JSR203)的异步I/O功能，扩展的文件系统属性，以及文件系统通知。<br />3. 使用新fork/join框架的java.util.concurrent新功能。<br />4. 一个新的平衡性垃圾回收(GC)策略，它会使于大型堆中的暂停时间短暂且保持一致。<br />5. 更详细且更有效的GC详情格式。<br />6. 重要的诊断提升，包括对javacore.txt内容(极值，本地栈，以及本地内存使用)的改进。<br />7. 对跟踪能力的改进，这一改进允许在任一跟踪点捕获Java栈路径。<br />8. 改进了将错误信息记录到操作系统日志，如Linux中的syslog，的功能。<br />9. 改进了对共享类缓存的支持，例如：附加内容，对缓存缓崩溃更好的诊断能力，查找并销毁缓存的可编程接口，能更好地控制持久化缓存文件许可，以及对展示缓存内容的更多的控制。<br /><br /><strong><span style="font-size: 12pt;">实时评估技术</span></strong><br />IBM Java SDK 7还包括一个实时增量GC策略(-Xgcpolicy:metronome)，Metronome，但目前只作评估之用。虽然该策略还不可用于Java 7许可协议，但在<a href="http://www.ibm.com/software/webservers/realtime/">WebSphere Real Time</a>产品中已获支持。<br /><br /><strong style="font-size: 12pt;">重要的改变</strong><br style="font-size: 12pt;" />1. 默认的GC策略变为分代并发垃圾收集策略(-Xgcpolicy:gencon)。<br />2. 如果安装了受支持的AIX版本，AIX会默认使用持久化的共享类缓存，以替换非持久化缓存。<br />3. 俄罗斯提议在10月份对夏令时进行修改，如果该提议被采纳，将需要更新到Java 7的时区数据。受此变化影响的客户可以使用IBM的<a href="http://dwmaster.raleigh.ibm.com/developerworks/java/jdk/dst/jtzu.html">Java Time Zone Update工具(JTZU)</a>来应用这一更新。关于使用该工具进行时区更新的信息，请参见<a href="http://www.ibm.com/developerworks/java/jdk/dst/olson_table.html">http://www.ibm.com/developerworks/java/jdk/dst/olson_table.html</a>。<br /><br /><strong><span style="font-size: 12pt;">更多信息</span></strong><br />针对IBM Java 7 SDK和JRE的用户文档可到<a href="http://publib.boulder.ibm.com/infocenter/java7sdk/v7r0/index.jsp">IBM信息中心</a>获取。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/360010.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-10-04 16:34 <a href="http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java SE 7发布：2006年12月以来的首个主版本升级(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 30 Jul 2011 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/355420.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/355420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/355420.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java SE 7发布：2006年12月以来的首个主版本升级</span></strong></div><span style="font-size: 10pt;">在经过漫长地等待之后，Java SE 7终于发布了。<a href="http://www.infoq.com/news/2011/07/javase7-ga">Info的这篇文章</a>总结了JDK 7的关键新特性，希望对大家了解JDK有帮助。(2011.07.30最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 从今天开始就可以使用Java 7了，这是Oracle收购Sun之后Java平台发布的第一个版本。该版本包含一系列很小但很受欢迎的语言变化，含有一个新的文件API及Fork/Join框架，改进了JVM对动态语言的支持。 </span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 7的语言级变化已成为Coin项目(<a href="http://jcp.org/en/jsr/summary?id=334">JSR 334</a>)的一部分，该项目是<a href="http://jcp.org/en/jsr/summary?id=201">JSR 201</a>的后继者，它被设计成使用一组能改善生产率的细小变化来提升Java语言。特别值得注意地是对"try-with-resources"的介绍。这是对C#的using语句的模仿，但是基于try语句的形式。结果，using语句只能处理单一资源，而try-with-resources能够在给定语句块的范围内处理多个资源。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 有两个针对异常处理的变化。第一，多个异常类型能够由单个try语句块处理。扩展了try语句中catch子句的语法，使它能处理一系列的异常类型，这些异常类型由"OR"操作符"|"分隔开，这将用于异常参数的声明处。第二，如果一个异常参数未被修改，且未在cactch语句块中被重新抛出，就不必把该异常加到方法签名中。</span><br /><span style="font-size: 10pt;">其它的语言级变化还包括：</span><br /><span style="font-size: 10pt;">1. String类型可用于switch语句</span><br /><span style="font-size: 10pt;">2. 支持二进制常量，且能在数字中使用下划线进行分隔，例如，long creditCardNumber = 1234_5678_9012_3456L</span><br /><span style="font-size: 10pt;">3. 简化的可变长参数方法的调用：当包含可变长参数的方法使用了不确定的数组类型时，编译器会报出警告。现在将该警告从方法调用处移到了方法声明处。</span><br /><span style="font-size: 10pt;">4. 改进了泛型实例创建时的类型推导(&lt; &gt;，即钻石符)：在创建类实例时使用受限制的类型推导，以便显式地为构造器声明参数类型，但从上下文可以确定这些类型，那么可用一个空的类型参数设置去替代这一声明。所以，为了替代如下写法：</span><br /><span style="font-size: 10pt;">Map&lt;String, List&lt;String&gt;&gt; anagrams = new HashMap&lt;String, List&lt;String&gt;&gt;();</span><br /><span style="font-size: 10pt;">可以写成：</span><br /><span style="font-size: 10pt;">Map&lt;String, List&lt;String&gt;&gt; anagrams = new HashMap&lt;&gt;();</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 已经介绍过两个主要的新API。第一个是<a href="http://jcp.org/en/jsr/summary?id=203">JSR 203</a>，这个API的加入是为了文件系统的访问，可伸缩的异步I/O操作，Socket-Channel绑定与配置，以及多播数据报。特别能引起企业级开发者兴趣的是真实的异步I/O API。对于那些会跨许多连接且要求低延时、高吞吐的高级服务器端应用，该API显得尤为重要。作为加入Java的最后一个文件系统API，JSR 203还支持某些操作系统的特殊功能。例如，你可以为那些支持符合链接的系统创建这种链接。因此，JSR 203有些争议，它并不严格遵循"编写一次，随处运行"这一原则，尽管它提供了一组能运行在所有平台上的通用API，但它也提供了某些平台的某些特定功能。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 第二个新API就是Fork/Join框架(它是<a href="http://jcp.org/en/jsr/summary?id=166">JSR 166</a>的一部分)，原计划是放到Java 5中的。它为开发者提供了一种将问题分解成任务的机制。这些任务可在任意数量的多核处理器中并发执行。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在其它方面，平台寻求新的网络与安全特性，并扩展了对国际化的支持，包括对Unicde 6.0的支持。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 最后，Java SE 7还引入自Java起始以来最新的字节码结构，<a href="http://jcp.org/en/jsr/detail?id=292">InvokeDynamic</a>关键字。InvokeDynamic加入了多于一个的调用模式，以及多于一个的链接模式，这样就能基于用户定义的规范进行编程。它的本意是在缺少静态类型信息时能高效且灵活地执行方法调用，这就使得运行在JVM上的动态语言，如JRuby和Jython，的性能得到实质性地提升。</span><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在欢迎这些特性的同时，从许多方面看来，对于该版本最重要的事情，是它真的终于发出来了。在Sun和Apache软件基金会之间长期存在的分歧，可能还有Sun在它最后的日子中的领导力与资源分配方面的问题，导致了这次版本间不同寻常的长期断层。就如Mark Reinhold在最近的一次<a href="http://medianetwork.oracle.com/media/show/16796">访谈</a>中所评论的：</span><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; "><img src="http://www.blogjava.net/Images/dot.gif" alt="" />由于商业与政治方面的原因，Java在一段时间内进行了冬眠模式。但我们又回来了，并且将要发布版本7。它不是一个革命性的版本，只是一个改进的版本，但它里面确实有一些好东西。</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开发者们对JDK 7版入门的兴趣，就会撬动对<a href="http://netbeans.org/community/releases/70/">NetBeans 7.0</a>与<a href="http://www.jetbrains.com/idea/">IntelliJ IEDA 10.5</a>的应用，它们都支持Java SE 7平台的最新特性。<a href="http://www.eclipse.org/indigo/">Eclipse Indigo</a>的<a href="http://wiki.eclipse.org/JDT/Eclipse_Java_7_Support_%28BETA%29">beta版也已支持Java 7</a>，Oracle的JDeveloper计划在今年晚些时候发布一个版本去支持JDK 7。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/355420.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-07-30 22:23 <a href="http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.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>James Gosling现在是Google人了(泽)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 29 Mar 2011 16:08:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/347244.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/347244.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/347244.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">James Gosling现在是Google人了</span></span></strong><br />
</div>
<span style="font-size: 10pt;">Java之父<a href="http://nighthacks.com/roller/jag/">James Gosling</a>已进入Google工作了，JavaLobby社区的一篇<a href="http://java.dzone.com/news/james-gosling-now-googler">文章</a>对该事件进行了评论，大家也可以发表自己的看法。(2011.03.30最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 人们已经问了几个月的问题--"Google会是新的Sun吗？"当然，你仍然可以争辩道这个答案为"否"，但它刚刚变成更为困难的争论了，因为James Gosling<a href="http://nighthacks.com/roller/jag/entry/next_step_on_the_road">刚刚告诉我们</a>，他开始了在Google的第一天工作。<br />
&nbsp;&nbsp;&nbsp; 这份简短声明今晨展示在Gosling的博客中，它使Java世界完全震惊了：<br />
&nbsp;&nbsp;&nbsp; "今天，我开始了在Google的工作。人生中最棘手的事情之一就是作出抉择。我有过一段困难的时光，在这段时间里我拒绝了许多其它极好的机会。我发现很奇怪的是，在这段时间里，我<a href="http://www.bartleby.com/119/1.html">游走了更多的地方</a>，但看起来这是有着极大影响且十分有趣的事情。"<br />
&nbsp;&nbsp;&nbsp; 还没有关于James所从事具体工作的消息，但如果这份工作邀请与Oracle针对Android的诉讼有关，我一点儿也不会感意外。但我可以肯定，一大堆公司曾经向Gosling大献殷勤，希望Gosling能加入他们的行列。Gosling在去年处于失业状态不太可能是一个巧合。可能是他处于与Oracle的一年非竞业合同的限制之下。<br />
&nbsp;&nbsp;&nbsp; 虽然Oracle可能拥有Gosling在Sun时曾经开发出来的所有一切，但还有一种可能的途径能使他帮助Google摆脱与Oracle的司法争吵。他的专利之一(RE38,104)是Oracle当前专权侵权诉讼的一部分。当然，我并不是说Google不想要Gosling丰富的程序设计专业知识以及创造精神。我可以肯定，Google能理解到它不是仅仅要把Gosling当作专利保护伞而以--但是无法保证的是，Gosling确实能扭转来自于Oracle的侵权主张。<br />
&nbsp;&nbsp;&nbsp; 这一变化会让一些人感到惊讶，因为当Android首次出现时，Gosling对Google进行了<a href="http://www.dzone.com/links/r/the_shit_finally_hits_the_fan_gosling_on_oracle_v.html">批评</a>。他也预测到，Android在维持与所以使用该开源软件的手机厂商关系方面存在着问题。<br />
&nbsp;&nbsp;&nbsp; 若有与这桩雇佣事情有关的任何消息出现，我们会让您知道的。<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/347244.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-03-30 00:08 <a href="http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>