﻿<?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-Jiangshachina-随笔分类-JavaSE</title><link>http://www.blogjava.net/jiangshachina/category/16089.html</link><description>同是Java爱好者，相逢何必曾相识！&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;a cup of Java, cheers!</description><language>zh-cn</language><lastBuildDate>Mon, 09 Nov 2015 00:28:00 GMT</lastBuildDate><pubDate>Mon, 09 Nov 2015 00:28:00 GMT</pubDate><ttl>60</ttl><item><title>Play OpenJDK: 允许你的包名以"java."开头(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2015/11/01/428010.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Java并发基础实践--死锁(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/12/29/408180.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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并发基础实践--退出任务I(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/09/21/404269.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Custom Layout Manager: PyramidLayout(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/07/15/383156.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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>15</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">Sha 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>IBM Java SDK 7(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha 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>Sha Jiang</dc:creator><author>Sha 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">Sha Jiang</a> 2011-05-31 20:46 <a href="http://www.blogjava.net/jiangshachina/archive/2011/05/31/351369.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaOne and Oracle Develop 2010(Beijing)--Day 2(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 16 Dec 2010 12:52:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/340835.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/340835.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/340835.html</trackback:ping><description><![CDATA[<br />
<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">JavaOne and Oracle Develop 2010(Beijing)--Day 2</span></strong></span><br />
</div>
<span style="font-size: 10pt;">本文是我参加<a href="http://www.oracle.com/cn/javaonedevelop/index.html">JavaOne与Oracle开发者大会(北京)</a>第二天活动的一些见闻与感受，有图有真相，希望大家能喜欢。(2010.12.16最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; Day 2？是的，由于我周一比较忙，就没有参加Day 1的活动，所以Day 2才开始参会。不过Day 1的活动是下午才开始的，而且主要与开幕相关，技术专题都还没开始，所以是否参加都无所谓。<br />
<br />
<strong><span style="font-size: 12pt;">1. 赴会</span></strong><br />
&nbsp;&nbsp;&nbsp; 周一确实很忙，加班到半夜，一点钟才回到家中，洗漱完毕后又看了会儿电视，二点钟才睡下。清晨六点，闹钟将我吵醒，挣扎了十分钟后，我还是翻身起床了...吃完了老妈煮的排骨面，我出门了。北京最近正在降温，清早的温度更低，还好穿了一件较厚的羽绒服，呼吸着冰冷的空气，我的精神还不错。<br />
&nbsp;&nbsp;&nbsp; 我住在北五环外闻名京城的某大型"人文"社区，去会议现场--<a href="http://www.cnccchina.com/">国家会议中心</a>，全程只需要坐城铁/地铁就可以了，交通还算方便。但我遇上了早高峰(我平时是坐公司班车，不用挤城铁)，经历了一番艰难险阻(此处省去100万字)之后，我终于"被"挤进了城铁的车厢...又转了两次地铁之后，终于到了奥林匹克公园站，出站后再走上几步就到了此行的目的地--<a href="http://www.cnccchina.com/">国家会议中心</a>。<br />
<span style="font-size: 12pt;"><strong><br />
2. 会场印象</strong></span><strong></strong><br />
&nbsp;&nbsp;&nbsp; 为了防止恐怖分子，进会场之前要安检，包包和身体都被检测了一番。在工作人员的引导下，我进行了会议注册。举办方为每个参会者发了一个黑色的背包，里面装有会议指南，笔记本，水性笔，还有一个水杯。我觉得笔和水杯都太次了;-) 值得提及的是，我在参会之前的抽奖活动中赢得了一个2G版的<a href="http://www.apple.com.cn/ipodshuffle/">iPod Shuffle</a>，参加了多次技术会议，还是第一次中奖:-D<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/ipod.jpg" height="450" width="600" /><br />
<br />
&nbsp;&nbsp;&nbsp; 甫一进入会场，我就感到Oracle的手笔比Sun大多了(不过，咱交的<a href="http://www.oracle.com/cn/javaonedevelop/javaone-register-cn-161000-zhs.html">会费</a>也多了好几倍呢)。整个会场很大，但会场内各处的工作人员也很多，考虑得还是比较贴心的。由于是第一次到国家会议中心，对它的布局一点儿都不熟悉，看着展板中各个主题或技术专题会场的房间代码，我很是有点儿晕，所以举办方设置了多个咨询处，当然，我也可以随时咨询身边的工作人员。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/hall_01.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/hall_02.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/hall_03.jpg" alt="" border="0" height="450" width="600" /><br />
<br />
<strong><span style="font-size: 12pt;">3. 主题演讲/技术专题</span></strong><br />
&nbsp;&nbsp;&nbsp; 我上午主要就是在大会堂(比人民大会堂小多了)里听了两场主题演讲。首先是Oracle的两位VP，分别介绍了Oracle的最新技术更新情况，软硬件均有涉及。从演讲的内容来看，云计算是重中之重，即所谓的<a href="http://www.oracle.com/us/products/middleware/exalogic/index.html">Exalogic Elastic Cloud Computing</a>，还特别推荐了<a href="http://www.oracle.com/us/products/database/exadata/index.html">Exadata存储服务器</a>，应该也与云计算有关。第二场演讲是神州数码(God，神马就是浮云？！)的老总作的关于数据城市的主题。不得不说，国内老总的官派作风严重，我本来对这个题目就没兴趣，结果这位老总说得又慢又没劲，我没听完就走了。<br />
&nbsp;&nbsp;&nbsp; 下午我仍然听了几场主题演讲，首先是，Oracle软件部的一位高级总监介绍了Java的发展状况。按照以前参加SunTechDays的经验，一般都会有类似的主题。主要就是介绍Java当前有哪些主要的项目，各个项目的发展前景如何，会有哪些特性，计划在什么时候发布。我印象比较深的主要是<a href="http://openjdk.java.net/">OpenJDK</a>，以及其中的<a href="http://openjdk.java.net/projects/coin/">Coin项目</a>，它是关于一些小的Java语言级变化，为了提高劳动生产率。还有<a href="http://openjdk.java.net/projects/lambda/">Lambda项目</a>，它是关于闭包的。原计划闭包会出现在JDK 7中，但现在只可能出现在JDK 8中了。<br />
&nbsp;&nbsp;&nbsp; 听完这个主题演讲后，我就去听了一个技术专题--使用Oracle数据库构建高性能应用程序的最佳实践。我没听到开头，讲师是一位老黑，不知原本是哪国人，英语讲得很蹩脚。我现在只记得几个技术点，大约是说使用连接池，不要用绑定变量，还要使用某种Cache，这样就能提高性能。讲师对自己都很不满意，他在结束时还不好意思地笑着问大家有没有听懂他在说什么。哈哈，还是满可爱的嘛:-D<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/inner_00.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/inner_03.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/inner_05.jpg" alt="" border="0" /><br />
<br />
<strong><span style="font-size: 12pt;">4. 展厅</span></strong><br />
&nbsp;&nbsp;&nbsp; 会场内有一个展示大厅，里面设有Oracle，以及各个会议赞助商的展台。一片嘈杂的景象，我闲逛了一番，但没找到自己感兴趣的东西，只是在其中的网吧里上了会儿网，向公司里的同事发了几封邮件。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/pavilion_01.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/pavilion_02.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/pavilion_03.jpg" alt="" border="0" /><br />
<strong><span style="font-size: 12pt;"><br />
5. 午餐</span></strong><br />
&nbsp;&nbsp;&nbsp; 举办方为每位参会者准备了免费的午餐(世界上真有免费的午餐吗？)，我们凭每天特定的餐劵进入餐厅吃饭。餐厅很大，可同时容纳数以千记的人吃饭。午餐是自助餐，不得不说，交得会费多，午餐也就会丰盛些。想想以前参加SunTechDays时，午餐就是一份盒饭，既吃不饱，也吃不好。自助餐里中式菜的味道一般，特别是羊排完全没煮烂，每咬一口都要费很大力(绝不夸张)。还好我拿得不多，勉强吃完了，然后又拿了一些水果和甜点，这些还不错。另有咖啡和汤，我就没拿了，不想吃太多。总之，我对午餐表示满意(我这人很好养活的)。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/2010_02_food_01.jpg" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2010/2010_02_food_02.jpg" alt="" border="0" height="450" width="600" /><br />
<br />
<strong><span style="font-size: 12pt;">请关注我的系列博文--<a href="http://www.blogjava.net/jiangshachina/category/27188.html">JavaOne/SunTechDays</a></span></strong><br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/340835.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-12-16 20:52 <a href="http://www.blogjava.net/jiangshachina/archive/2010/12/16/340835.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaOne and Oracle Develop 2010 (Beijing)--Preview(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 12 Dec 2010 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/340183.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/340183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/340183.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;">
<span style="font-size: 14pt;">JavaOne and Oracle Develop 2010 (Beijing)--Preview</span></span></strong><br />
</div>
<span style="font-size: 10pt;">
自从<a href="http://www.oracle.com/us/corporate/press/018363">Sun被Oracle收购</a>之后，往常一年一度的SunTechDays就离我们而去了，但它以另一种形式--<a href="http://www.oracle.com/cn/javaonedevelop/index.html">JavaOne与Oracle开发者大会(北京)</a>--回到了我们的身边。本次大会即将在下周开幕，在此之前请允许我先列举一下我个人感兴趣的若干主题。(2010.12.12最后更新)<br />
<br />
<a href="http://www.oracle.com/cn/javaonedevelop/index.html">JavaOne与Oracle开发者大会2010(北京)</a>下周就要与大家见面了，其实与本次会议同时进行的还有Oracle全球大会2010(北京)(大家肯定知道Oracle的<a href="http://www.oracle.com/us/openworld/splash/index.html">Openworld</a>吧)。这两个会议相对独立，可以同时注册，也可以只参加其中任何一个，当然，参会费将有所不同。由于我只参加JavaOne与Oracle开发者大会2010(北京)，故本文中的内容将只涉及这一会议中的相关主题。各个主题的的<a href="https://oraclecn.wingateweb.com/scheduler/eventcatalog/eventCatalog.do">简介</a>我均摘抄自本次会议的官方网站，最后我还会混说几句^_^<br />
<br />
<strong><span style="font-size: 12pt;">1. Java程序设计语言</span></strong><br />
<strong>Coin项目：针对JDK的微小语言变化</strong><br />
&nbsp;&nbsp;&nbsp; <a href="http://openjdk.java.net/projects/coin/">Coin项目</a>旨在通过选择和实施一组微小的语言变化来提高程序员的JDK生产力。Coin项目语言变化包括改进的整数值、字符串转换和尖括号输入。本专题讲座将介绍这些语言变化并演示IDE支持。此外还将讨论通用语言演变的选择过程和标准。<br />
<br />
<strong>JDK 7和Java SE 7</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将展示Oracle工程师和外部人员为JDK 7和Java SE 7开发的一些主要特性。然后将展望一下未来版本的可能特性。<br />
<br />
<strong>在JDK中使用文件系统API</strong><br />
&nbsp;&nbsp;&nbsp; JDK不久将包含一个新的文件系统API，用于解决许多长期存在的问题和java.io.File局限性问题。本专题讲座将介绍该API并说明它可以如何用于：* 访问文件* 控制路径* 复制和移动文件* 处理符号链接* 执行递归操作* 访问文件权限和其他属性* 监控文件系统* 开发自己的定制文件系统* 其他我们将通过实例来说明如何有效使用该API来构建广泛使用文件系统的工具和应用程序。<br />
<br />
<strong>代码签名最佳实践</strong><br />
&nbsp;&nbsp;&nbsp; 代码签名对于开发人员来说极具吸引力，因为它能为Java应用或扩展提供通常没有的更多权限。然而，有时最佳的决策却是不用代码签名，因为这会改善用户体验，并且应用程序可在安全沙盒中自动运行。 本专题讲座将介绍代码签名最佳实践，探讨何时进行代码签名并提供其部署技巧。我们还将介绍使用户能够创建更引人注目的非签名应用程序的新特性以及即将推出的与代码签名相关的JDK特性，例如签名模块。<br />
<br />
<strong><span style="font-size: 12pt;">2. Java虚拟机</span></strong><br />
<strong>Oracle的Java虚拟机战略</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座介绍HotSpot和Oracle JRockit这两个通用的Oracle Java虚拟机 (JVM)。讲座将讨论两种实施的优缺点，给出选择建议&#8212;&#8212;特别是采用了Oracle融合中间件产品时，并介绍融合为一个统一JVM的路线图。<br />
<br />
<strong>我的应用程序怎么了：Java虚拟机监控工具</strong><br />
&nbsp;&nbsp;&nbsp; 随着多核系统的广泛使用，开发人员面临着新的问题，如CPU数量增加时性能下降。这些大规模问题往往是由于使用传统调试器和分析器时对局部事件的不可见（缓存丢失、锁定模式等）造成的。本专题讲座将介绍一个新的开发工具，它准确记录并巧妙显示线程、锁、处理器和内存之间的交互。演示将展示如何：* 监控JDK 6、JDK 7和Java实时系统* 使用分布视图来快速识别风险* 在出现特定问题时使用频率视图来探查* 使用调度视图来检查当时究竟发生了什么* 关联这些视图。<br />
<br />
</span><span style="font-size: 10pt;"><strong>垃圾回收流言终结者</strong><br />
&nbsp;&nbsp;&nbsp; 垃圾回收 (GC)
的采用促进了大型企业管理软件可靠性的提高和开发时间的缩短。但GC不是一个魔杖；尽管它可以消除许多种可能的程序员错误，但还有一些其他问题开发人员必
须加以注意在本专题讲座中，我们将对许多GC神话做一个公正的评估：它擅长什么和不擅长什么。尽管本讲座不会直接涉及某一特定产品，但它也值得所有使用
Java及依赖于Java虚拟机的其他语言（如Scala、Jython、Jruby等）编写程序并使用GC的程序员关注。<br />
<strong><br />
如何在Java虚拟机上调优和编写低延迟应用程序</strong><br />
&nbsp;&nbsp;&nbsp; 许多软件应用程序都需要低延迟响应。这些应用程序的许多使用者都希望利用Java SE所提供的开发和部署优势。随着Java虚拟机 (JVM)
的最新发展，许多有低延迟要求的应用程序都可以使用Java
SE来实施。本专题讲座将介绍如何针对这些应用程序和编程结构调优现代JVM，以及可实现低延迟响应的Java SE数据结构最佳实践。</span><br />
<span style="font-size: 10pt;">
<br />
<strong><span style="font-size: 12pt;">3. Oracle</span></strong><br />
<strong>使用Oracle数据库构建高性能应用程序的最佳实践</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将探讨使用Oracle数据库11g构建高性能应用的设计和编码最佳实践，并将涉及数据库访问层。本讲座还将演示Oracle数据库11g工具，如AWR和ADDM，突出它们所提供的卓越的应用程序性能洞察力，并展示这些工具如何在开发周期初期以及应用程序部署阶段帮助识别应用程序的问题。虽然介绍的是Java和C/C++用例，但所述技术还适用于其他语言，如PHP、Ruby、Python和Perl。本专题讲座适合于希望了解如何提升基于数据库的应用程序性能的任何人，无论他们使用何种编程语言。<br />
<br />
<strong>部署一个"隐形"的数据库：Oracle数据库11g第2版</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将详细介绍如何在嵌入式环境中部署具有行业优势的完整数据库。您将了解到如何使用Oracle数据库11g第2版目前提供的可用工具和功能将该数据库嵌入您的应用程序中，使之对最终用户完全透明。本专题讲座还将探讨嵌入式数据库的静默安装、管理和可用性。<br />
<br />
<strong>面向开发人员和DBA的强大Oracle SQL Developer报表</strong><br />
&nbsp;&nbsp;&nbsp; 大多数数据库管理员都有数据库健康检查和查看数据库状态的脚本，而应用开发人员和数据库开发人员则通过运行报表来监控其数据状况和检查其所用的数据库结构。Oracle SQL Developer为检查和查询实例及系统数据提供了一组现成报表。通过应用额外的过滤器，并进行相应的数据排序，可以审查和调整数据展现表格。本专题讲座将向用户展示如何利用这些报表和支持数据表格的特性。您将了解如何自建报表，包括图形化显示、主从报表、传递参数和钻取功能。<br />
<br />
<strong>使用内存数据库技术的高性能SQL应用程序</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将就Oracle TimesTen内存数据库和Oracle内存数据库高速缓存的产品功能进行技术讨论和演示。了解如何使用JDBC、ODBC、.NET、OCI、 Pro*C和PL/SQL等标准SQL和数据库API来显著提高应用程序响应速度和吞吐量。本专题讲座将揭示如何使用内存数据库高速缓存网格有效扩展应用程序，从而扩大事务吞吐量和高速缓存数据的有效尺寸。对于需要利用易于理解和广泛使用的数据库API来最大限度地提高新应用程序和现有应用程序性能的数据库开发人员，本专题讲座是最佳选择。<br />
<br />
<strong>使用内存数据库技术的高性能SQL应用程序</strong><br />
&nbsp;&nbsp;&nbsp; 本专题讲座将就Oracle TimesTen内存数据库和Oracle内存数据库高速缓存的产品功能进行技术讨论和演示。了解如何使用JDBC、ODBC、.NET、OCI、 Pro*C和PL/SQL等标准SQL和数据库API来显著提高应用程序响应速度和吞吐量。本专题讲座将揭示如何使用内存数据库高速缓存网格有效扩展应用程序，从而扩大事务吞吐量和高速缓存数据的有效尺寸。对于需要利用易于理解和广泛使用的数据库API来最大限度地提高新应用程序和现有应用程序性能的数据库开发人员，本专题讲座是最佳选择。<br />
<br />
<strong><span style="font-size: 12pt;">4. MySQL</span></strong><br />
<strong>MySQL 5.5版中性能和可伸缩性的增强 </strong><br />
&nbsp;&nbsp;&nbsp; 本技术专题讲座深入探讨了MySQL服务器和InnoDB 1.1插件中新增的性能特征。其中包括对多核系统获得的可伸缩性的详细介绍。<br />
<br />
<strong>MySQL性能调优最佳实践</strong><br />
&nbsp;&nbsp;&nbsp; 了解如何只利用MySQL附带的工具进行MySQL的基本调优。了解如何对连接进行调优以及选择最佳存储引擎，了解如何分析MySQL服务器的关键统计数据。本专题讲座是一个技术讲座，但无需具备MySQL经验。<br />
<br />
<strong>MySQL的战略：下一步计划是什么？</strong><br />
&nbsp;&nbsp;&nbsp; 本讲座将介绍MYSQL社区、产品和战略的最新信息，探讨MYSQL如何与Oracle全面、开放且集成的战略相一致。结识领先的MySQL开发者，了解MySQL的下一步开发计划。<br />
&nbsp;&nbsp;&nbsp; <em>笔者：由于Sun被Oracle收购后，MySQL就很自然地落入了Oracle的袋中，从那时起，整个社区对MySQL的前景就执悲观态度。MySQL算是我最熟悉的主流数据库了，但也很长时间没太关注了，这次就顺便听听Oracle将如何安排MySQL的命运。</em><br />
<br />
<strong><span style="font-size: 12pt;">5. Embedded/Mobile</span></strong><br />
<strong>我的朋友在哪里：Java ME定位API实践</strong><br />
&nbsp;&nbsp;&nbsp; 在本上机操作中，参会者将创建一个使用Java平台微型版(Java ME)定位API的移动应用程序，以跟踪其朋友的位置并在地图上显示。此外，该移动应用程序还支持朋友之间的文本消息传递，并会在地图上显示他们以及他们的照片和名字。本专题讲座适合希望了解如何将应用程序的内容与应用程序用户的当前位置进行捆绑的开发人员。参会者将了解：- 如何使用Java ME定位API- 如何在Java ME应用程序中使用谷歌地图服务- 如何使用Java ME通用连接框架- 如何使用可视化工具来设计应用程序的流和UI。<br />
</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <em>笔者：我一直都没关注过嵌入式或移动类应用，但现在这样的应用越来越普遍了，身边不少同事与朋友也在使用iPhone，或基于<a href="http://www.android.com/">Android</a>的智能手机，这都显示出手机应用的巨大市场潜力。而基于位置的移动应用也是方兴未艾(玩过<a href="http://foursquare.com/">Foursquare</a>吗？)，所以我这次也想去试试水。不过遗憾的是，这次没有<a href="http://www.android.com/">Google Android</a>的相关主题，难倒还是由于Oracle对Google的侵权诉讼？！大家还是应以和为贵。</em></span><br />
<span style="font-size: 10pt;">
<br />
<strong><span style="font-size: 12pt;">6. 杂想</span></strong><br />
&nbsp;&nbsp;&nbsp; 当年Sun被Oracle收购时，我还颇有几分感慨。无论是Java程序设计语言，还是<a href="http://www.sparc.org/">SPARC</a>处理器，以及<a href="http://hub.opensolaris.org/bin/view/Main/">Solaris/OpenSolaris</a>，在技术上都是同类中的佼佼者，但在商业上都未能成功。在软件方面，作为Java的创造者，Sun从Java中获得的收益不如IBM，还不得不开源了它的主要软件产品(如<a href="http://hub.opensolaris.org/bin/view/Main/">Solaris</a>，<a href="http://www.netbeans.org/">NetBeans</a>)，甚至于Java也被迫开源了--<a href="http://openjdk.java.net/">OpenJDK</a>。而在硬件方面，Sun的服务器，SPARC处理器也是曲高和寡，市场占用率很低。<br />
&nbsp;&nbsp;&nbsp; 此外，对Java发展有重要贡献的人物，如<a href="http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683">Effective Java</a>的作者<a href="http://en.wikipedia.org/wiki/Joshua_Bloch">Joshua Bloch</a>，Java之父<a href="http://nighthacks.com/roller/jag/">Games Gosling</a>，也相继离开了Sun/Oracle。回想当年，我第一次参加SunTechDays(JavaChina 2005)时，还有幸与Games Gosling有一张合影。可惜，在Oracle举办的会议中也许就再也看不到这位和蔼的大胡子老爹了;-(<br />
<br />
&nbsp;&nbsp;&nbsp; 由于工作原因，我不会参加明天(2010.12.13)下午的开幕式，不过明天下午的专题本来也很少，所以不参加也关系不大。我会为每一天的参会经历写一篇博文，记录自己的所见，所闻，所思，所感，还会附上图片，请大家持续关注。<br />
<strong><span style="font-size: 12pt;"><br />
请关注我的系列博文--<a href="http://www.blogjava.net/jiangshachina/category/27188.html">JavaOne/SunTechDays</a>。</span></strong><br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/340183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-12-12 20:55 <a href="http://www.blogjava.net/jiangshachina/archive/2010/12/12/340183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--多线程编程(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 20 Nov 2010 15:49:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/338571.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/338571.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/338571.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">你所不知道的五件事情--多线程编程</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中<a href="http://www.ibm.com/developerworks/java/library/j-5things15/index.html">5 things系列文章中的一篇</a>，讲述了关于多线程的一些应用窍门，值得大家学习。(2010.11.22最后更新)<br />
<br />
摘要：多线程编程不轻松，但它确实能帮助理解JVM如何细微地处理不同代码结构。Steven Haines将分享的5个窍门会帮助你在处理同步方法，volatile变量以及原子类时做出更为合理的决定。<br />
<br />
&nbsp;&nbsp;&nbsp; 尽管很少有Java开发者能够忽略多线程编程，且Java平台类库支持它，甚至于更少的开发者能有时间去深入学习线程。相反，我们只是泛泛地学习线程，如果需要的话，会向我们的工具箱中添加新的技巧和技术。通过这种方法你可能会构建且运行好的应用程序，但你还能做得更好。理解Java编译器和JVM的线程特性，可以帮助你编写更高效，性能更佳的Java代码。<br />
&nbsp;&nbsp;&nbsp; 在<a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=5+things+you+did">5 things系列</a>的本期文章中，我会介绍一些使用同步方法，volatile变量和原子类等多线程编程的细节方面。我的讨论特别关注在这些程序结构是如何与JVM和Java编译器进行交互的，以及不同的交互是如何影响Java应用程序性能的。<br />
<br />
<strong><span style="font-size: 12pt;">1. 同步方法与同步块</span></strong><br />
&nbsp;&nbsp;&nbsp; 你偶尔会衡量是否同步整个方法调用，或者只是同步方法中线程安全的子块。在这种情况下，知道Java编译器在何时将源代码转化为字节码是有帮助的，它在处理同步方法和同步块时是完全不同的。<br />
&nbsp;&nbsp;&nbsp; 当JVM在执行同步方法时，执行线程标识方法的method_info结构设有ACC_SYNCHRONIZED标记，然后它自动地获取对象的锁，调用方法，再释放锁。如果发生了异常，线程会自动释放锁。<br />
&nbsp;&nbsp;&nbsp; 另一方面，同步一个方法块，绕开JVM内建的对获取对象锁和异常处理的支持，这些功能要显式的写在字节码中。如果你读过含有同步块的方法的字节码，你将看到更多的额外操作去管理该功能。清单1展示了生成同步方法与同步块所产生的调用：<br />
<br />
<strong>清单1. 两种同步方法</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.geekcap;<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;SynchronizationExample&nbsp;{<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;i;<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;synchronizedMethodGet()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;i;<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;synchronizedBlockGet()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">(&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">&nbsp;)&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;">&nbsp;i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
synchronizedMethodGet()方法生成下列字节码：<br />
</span>
<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: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_0<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;getfield<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;nop<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;iconst_m1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;ireturn</span></div>
<span style="font-size: 10pt;"><br />
而下面是synchronizedBlockGet()方法的字节码：<br />
</span>
<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: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_0<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;dup<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;astore_1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;monitorenter<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_0<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;getfield<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">6</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;nop<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">7</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;iconst_m1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">8</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">9</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;monitorexit<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">10</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;ireturn<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">11</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;astore_2<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">13</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;monitorexit<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">14</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_2<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">15</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;athrow</span></div>
<span style="font-size: 10pt;"><br />
创建同步块会产生16行字节码，然而同步方法只返回5行代码。<br />
<br />
<strong><span style="font-size: 12pt;">2. ThreadLocal变量</span></strong><br />
&nbsp;&nbsp;&nbsp; 如果你想为一个类的所有实例维护单个变量实例，你将使用静态类成员变量来实现这一点。如果你想在每个线程中维护一个变量的实例，你将使用thread- local变量。ThreadLocal变量不同于平常的变量，在于每个线程有它自己的变量初始化实例，通过get()或set()方法可以访问这些变量。<br />
&nbsp;&nbsp;&nbsp; 让我们说，你正在开发多线程代码追踪器的目的是从你的程序去唯一地标识每个线程的路径。挑战在于你需要在跨越多个线程的多个类中协调多个方法。没有 ThreadLocal，这将是一个很复杂的问题。当一个线程开始执行时，它将生成一个唯一的标记以便于在追踪器中进行标识，并在在路径中将这个唯一标记传给每个方法。<br />
&nbsp;&nbsp;&nbsp; 使用ThreadLocal，问题就变得简单了。线程在运行的开始时初始化thread-local变量，然后在各个类的各个方法中去访问它，这就能确保该变量只会在当前执行线程中维护路径信息。当线程执行完毕时，线程会将它的特定路径传递给一个管理对象，该对象负责维护所有的路径。<br />
&nbsp;&nbsp;&nbsp; 当你需要基于每个线程来存储变量时，使用ThreadLocal就很有意义。<br />
<br />
<strong><span style="font-size: 12pt;">3. volatile变量</span></strong><br />
&nbsp;&nbsp;&nbsp; 我估计一大半Java开发员知道Java语言含有关键字volatile。其中大约只有10%的人知道它的意义，只有更少的人知道如何高效地使用它。简言之，将一个变量使用volatile关键字进行标识就意味着该变量的值将被不同的线程修改。为了充分理解volatile关键字的功用，首先就会帮助我们理解线程是如何处理非volatile变量的。<br />
&nbsp;&nbsp;&nbsp; 为了改进性能，Java语言规范允许JRE在各个线程中维护一份针对某个变量的引用的复本。你能够认为这些变量的"thread-local"复本类似于缓存，这会帮助线程避免在每次需要访问该变量的值时都去检查主内存。<br />
&nbsp;&nbsp;&nbsp; 但考虑下面场景可能会发生的事情：两个线程都启动了，第一个线程读到变量A的值为5，而第二个线程读到变量A的值为10。如果变量A已经从5变到10了，然后第一个线程并不会意识到这一变化，所以它会得到A的错误值。如果变量A被标记为volatile，然后在任何时候，某个线程读取A的值时，它都将查询 A的主复本并读到它的当前值。<br />
&nbsp;&nbsp;&nbsp; 如果应用中的变量不会改变，那么使用一个thread-local缓存将是有意义的。另外，知道volatile关键字能为你做些什么也是很有帮助的。<br />
<br />
<strong><span style="font-size: 12pt;">4. volatile对于同步</span></strong><br />
&nbsp;&nbsp;&nbsp; 如果变量被声明为volatile，就意味着它会被多个线程所修改。很自然地，你会希望JRE能为volatile变量以某种方式强制执行同步。幸运地是，当访问volatile变量时，JRE隐式地提供了同步，但会伴随一个很大的代价：读volatile变量是同步的，写volatile变量也是同步的，但非原子性操作不能怎么做。<br />
&nbsp;&nbsp;&nbsp; 这就意味着下面的代码不是线程安全的：<br />
</span>
<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: #000000;">myVolatileVar</span><span style="color: #000000;">++</span><span style="color: #000000;">;</span></div>
<span style="font-size: 10pt;"><br />
前面的语句可以写成如下形式：<br />
</span>
<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;">int</span><span style="color: #000000;">&nbsp;temp&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
synchronize(&nbsp;myVolatileVar&nbsp;)&nbsp;{<br />
&nbsp;&nbsp;temp&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;myVolatileVar;<br />
}<br />
<br />
temp</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />
<br />
synchronize(&nbsp;myVolatileVar&nbsp;)&nbsp;{<br />
&nbsp;&nbsp;myVolatileVar&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;temp;<br />
}</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 换言之，如果一个volatile变量按上述方法来进行更新，即先读取值，并修改之，然后再赋值，在两个同步操作之间，这个结果是非线程安全的。你可以考虑是使用同步，还是依赖JRE对volatile变量的自动同步。更好的方法是根据你的用例：如果赋给volatile变量的值依靠于它的当前值(例如加法操作)，如果你想操作是线程安全的，那就必须使用同步。<br />
<br />
<strong><span style="font-size: 12pt;">5. 原子字段更新器</span></strong><br />
&nbsp;&nbsp;&nbsp; 当在多线程环境中加或减一个原始数据类型时，使用java.util.concurrent包中新添加的原子类会比编写你自己的同步代码块要好得多。原子类保证能以线程安全的方式来执行这些操作，如加减数值，更新值，以及添加值。原子类包括 AtomicInteger，AtomicBoolean，AtomicLong，AtomicLong等等。<br />
&nbsp;&nbsp;&nbsp; 使用原子类的挑战在于所有的类方法，包括get，set，以及get-set方法簇都是原子化的。这就意味着read和write操作不会以同步的方式来修改原子变量的值，也不仅仅重要的读-更新-写操作。如果你想对同步代码的发布能有更好的控制，解决方法就是使用原子字段更新器。<br />
<br />
<strong>使用原子更新</strong><br />
&nbsp;&nbsp;&nbsp; 原子字段更新器，如AtomicIntegerFieldUpdater，AtomicLongFieldUpdater和 AtomicReferenceFieldUpdater，是用于volatile字段的基本包装器类。在JDK的内部，Java类库就在使用这些原子类。但在应用程序中，它们还未被广泛使用，你也没有理由不使用它们。<br />
&nbsp;&nbsp;&nbsp; 清单2展示的示例，是一个类使用原子更新来改变某人正在阅读的书：<br />
<br />
<strong>清单2. Book类</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.geeckap.atomicexample;<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;Book<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;name;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Book()<br />
&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;Book(&nbsp;String&nbsp;name&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&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;String&nbsp;getName()<br />
&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;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(&nbsp;String&nbsp;name&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&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 />
}</span></div>
<span style="font-size: 10pt;"><br />
Book类只是一个POJO(Plain Old Java Object)，只有一个字段：name。<br />
<br />
<strong>清单3. MyObject</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.geeckap.atomicexample;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.concurrent.atomic.AtomicReferenceFieldUpdater;<br />
<br />
</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;*<br />
&nbsp;*&nbsp;</span><span style="color: #808080;">@author</span><span style="color: #008000;">&nbsp;shaines<br />
&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;MyObject<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">volatile</span><span style="color: #000000;">&nbsp;Book&nbsp;whatImReading;<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;AtomicReferenceFieldUpdater</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">MyObject,Book</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;updater&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AtomicReferenceFieldUpdater.newUpdater(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyObject.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;Book.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">whatImReading</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Book&nbsp;getWhatImReading()<br />
&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;whatImReading;<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;setWhatImReading(&nbsp;Book&nbsp;whatImReading&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">this.whatImReading&nbsp;=&nbsp;whatImReading;</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updater.compareAndSet(&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.whatImReading,&nbsp;whatImReading&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 清单3中的MyObject类揭露了whatImReading属性就是你所期望的，该属性有get和set方法，但set方法做的一些事情不太一样。不同于简单地将内部的Book引用赋予一个特定的Book对象(使用清单3中被注释的代码就可以做到这一点)，该示例使用了一个 AtomicReferenceFieldUpdater。<br />
<br />
<strong>AtomicReferenceFieldUpdater</strong><br />
Javadoc对AtomicReferenceFieldUpdater有如下定义：<br />
&nbsp;&nbsp;&nbsp; 一个基于反射的工具类，它能对指定类的指定的volatile引用字段进行原子更新。该类被设计用于原子数据结构，在这种结构中，相同节点的多个引用字段会进行独立地原子更新。<br />
&nbsp;&nbsp;&nbsp; 在清单3中，通过调用AtomicReferenceFieldUpdater的静态方法newUpdater就能创建它的实例，该方法要接收三个参数：<br />
&nbsp;&nbsp;&nbsp; 包含该字段的对象的类(在这个例子中，就是MyObject)<br />
&nbsp;&nbsp;&nbsp; 将被自动更新的对象的类<br />
&nbsp;&nbsp;&nbsp; 将被自动更新的字段的名称<br />
<br />
在执行getWhatImReading方法获取实际值时没有使用任何形式的同步，然而setWhatImReading方法的执行则是一个原子操作。<br />
&nbsp;&nbsp;&nbsp; 清单4证明了如何去使用setWhatImReading()方法，以及如何判断变量的值进行了正确地修改：<br />
<br />
<strong>清单4. 练习原子更新的测试用例</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.geeckap.atomicexample;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.junit.Assert;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.junit.Before;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.junit.Test;<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;AtomicExampleTest<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;MyObject&nbsp;obj;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Before<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;setUp()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MyObject();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setWhatImReading(&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Book(&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Java&nbsp;2&nbsp;From&nbsp;Scratch</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;)&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Test<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;testUpdate()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setWhatImReading(&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Book(<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;">Pro&nbsp;Java&nbsp;EE&nbsp;5&nbsp;Performance&nbsp;Management&nbsp;and&nbsp;Optimization</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;)&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Incorrect&nbsp;book&nbsp;name</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;</span><span style="color: #000000;">"</span><span style="color: #000000;">Pro&nbsp;Java&nbsp;EE&nbsp;5&nbsp;Performance&nbsp;Management&nbsp;and&nbsp;Optimization</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;obj.getWhatImReading().getName()&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}</span></div>
<span style="font-size: 10pt;"><br />
查看资源以学习更多关于原子类的知识。<br />
<br />
<strong><span style="font-size: 12pt;">结论</span></strong><br />
&nbsp;&nbsp;&nbsp; 多线程编程总是存在着挑战性，但涉及到Java平台，它已经获得了支持去简化一些多线程编程任务。在本文中，我讨论了你在基于Java平台编写多线程应用时可能不知道的五件事情，包括同步方法与同步块的不同之处，使用ThreadLocal变量为每个线程去存储值，针对volatile关键字的广泛误解 (包括在需要同步时依赖volatile所产生的危险)，还简要地看了一下原子类的复杂之处。查看资源以学习到更多相关知识。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/338571.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-11-20 23:49 <a href="http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--改进Swing(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 25 Oct 2010 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/336125.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/336125.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/336125.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 你所不知道的五件事情--改进Swing这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇，讲述了关于改进Swing应用的一些窍门，值得大家学习。(2010.10.25最后更新)摘要：Swing已是一个比较老的工具集了，在美观的用户界面出来之前需要开发很长时间。它缺少一些你在开发富UI时所需的组件。幸运地是，像 Substance，Swi...&nbsp;&nbsp;<a href='http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html'>阅读全文</a><img src ="http://www.blogjava.net/jiangshachina/aggbug/336125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-10-25 22:23 <a href="http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--JVM的命令行选项(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 01 Sep 2010 00:53:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/330341.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/330341.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/330341.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">你所不知道的五件事情--JVM的命令行选项</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是Ted Neward在<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中<a href="http://www.ibm.com/developerworks/java/library/j-5things11/index.html">5 things系列文章中的一篇</a>，讲述了关于JVM命令行参数的一些应用窍门，值得大家学习。(2010.09.01最后更新)<br />
<br />
摘要：Java虚拟机有数百个命令行选项，只有经验十分丰富的Java开发员才会使用这些选项去调优Java运行时环境。学习如何监控并记录编译器性能，禁用显示的垃圾收集(System.gc())，扩展JRE，及其它。<br />
<br />
&nbsp;&nbsp;&nbsp; JVM是Java应用程序功能与性能背后的实际工作者，大部分Java开发者认为这是理所当然的。然而我们中很少有人真正地理解JVM是如何做到这些事的--如，分配并收集垃圾对象，摆弄线程，打开及关闭文件，解释和/或使用JIT编译Java字节码，以及其它任务。<br />
&nbsp;&nbsp;&nbsp; 不熟悉JVM不仅会使你在应用程序的性能方面付出代价，而且当JVM出了某些问题时，还很难进行修复。<br />
&nbsp;&nbsp;&nbsp;
<a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=5+things+you+did">5 things系列</a>的这篇文章介绍一些好用的命令行JVM选项，你可以使用它们去诊断并调优Java虚拟机的性能。<br />
<br />
<strong><span style="font-size: 12pt;">
1. DisableExplicitGC</span></strong><br />
&nbsp;&nbsp;&nbsp; 我无法告诉你我已经多少次被要求就应用程序性能问题进行咨询了，而我只是简单地对代码文件执行grep命令就能发现如清单1所示的程序--这就是早期Java性能的反模式：<br />
<br />
<strong>Listing 1. System.gc();</strong><br />
</span>
<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: #008000;">//</span><span style="color: #008000;">&nbsp;We&nbsp;just&nbsp;released&nbsp;a&nbsp;bunch&nbsp;of&nbsp;objects,&nbsp;so&nbsp;tell&nbsp;the&nbsp;stupid<br />
</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;garbage&nbsp;collector&nbsp;to&nbsp;collect&nbsp;them&nbsp;already!</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">System.gc();</span></div>
<span style="font-size: 10pt;"><br />
显式地执行垃圾收集器<em>确实是个坏主意</em>--这就像把你自己与一个疯狂的斗牛犬锁在电话亭中那样。虽然该调用的确切语义依赖于具体的实现，但假设你的JVM运行着一个分代垃圾收集器(大多数JVM也正是如此)，System.gc()方法强制要求VM对Heap进行"全面清扫"，即便有些并不是必要的。一般地，全面清扫的开销要比常规GC操作要大几个数量级，而常规GC操作的开销只会产生纯数学的负作用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;
但不要相信我的话--为了这个特殊的人为错误，Sun的工程师们为我们提供了一个JVM选项：<span style="font-family: Courier;">-XX:+DisableExplicitGC</span>选项自动地将<span style="font-family: Courier;">System.gc</span>()调用置为无用操作，给你一个机会去执行程序，并让你自己看看<span style="font-family: Courier;">System.gc</span>()是否已经帮助或损害整个JVM的执行。<br />
<br />
<strong><span style="font-size: 12pt;">2. HeapDumpOnOutOfMemoryError</span></strong><br />
&nbsp;&nbsp;&nbsp; 你肯定曾经遇到过JVM不断死掉的情况，即抛出<span style="font-family: Courier;">OutOfMemoryError</span>，在你的一生中似乎都不能设置一个调试器去捕获这个错误，并看看是出了什么问题？像这些不时发生且/或无法确定的问题能使一个开发员完全疯掉。<br />
&nbsp;&nbsp;&nbsp; 你所想的，就是在在JVM快死掉时能够捕获Heap的快照--这正是命令<span style="font-family: Courier;">-XX:+HeapDumpOnOutOfMemoryError</span>所要做的。<br />
&nbsp;&nbsp;&nbsp; 运行该命令就是告诉JVM创建一个"Heap转储快照"，并将它保存为一个文件以便于处理，常常使用jhat工具(在<a href="http://www.ibm.com/developerworks/java/library/j-5things11/index.html#resources">前一篇文章</a>中我有介绍过)。使用相应的命令<span style="font-family: Courier;">-XX:HeapDumpPath</span>，就指定被保存文件的确切路径。(不论文件保存在何处，要先确保文件系统和/或Java进程有必要的仅限配置能在那儿写文件。) <br />
<br />
<strong><span style="font-size: 12pt;">3. bootclasspath</span></strong><br />
&nbsp;&nbsp;&nbsp; 偶尔地，将某个类，该类不同于原有的或以某种方式扩展而得到的JRE所存储的类，置于类路径中是有用的。(一个例子就是新的Java Crypto API适配器)。如果你想扩展JRE，那么你的定制实现需要能用于引导ClassLoader，该ClassLoader会加载<span style="font-family: Courier;">java.lang.Object</span>以及它在<span style="font-family: Courier;">rt.jar</span>中的所有同族成员。<br />
&nbsp;&nbsp;&nbsp; 尽管你可以打开<span style="font-family: Courier;">rt.jar</span>并将你定制实现或新的包放入其中，但在技术上这就违反了当初你在下载JDK时所同意的协议。<br />
&nbsp;&nbsp;&nbsp; 相反地，应该使用JVM自己的<span style="font-family: Courier;">-Xbootclasspath</span>选项，以及它的兄弟选项<span style="font-family: Courier;">-Xbootclasspath/p</span>和<span style="font-family: Courier;">-Xbootclasspath/a</span>。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">-Xbootclasspath</span>允许你设置整个引导类路径，该路径一般地必须包含一个针对<span style="font-family: Courier;">rt.jar</span>的引用，再加上一批随JDK发布但不是<span style="font-family: Courier;">rt.jar</span>一部分的其它JAR文件。<span style="font-family: Courier;">-Xbootclasspath/p</span>会向已有引导类路径中预置值，然后-Xbootclasspath/a会将它连接起来。<br />
&nbsp;&nbsp;&nbsp; 例如，如果你已经修改了原有的<span style="font-family: Courier;">java.lang.Integer</span>，并将修改后的程序放入子目录，<span style="font-family: Courier;">mods</span>，然后参数<span style="font-family: Courier;">-Xbootclasspath/a mods</span>将会在加载原有默认<span style="font-family: Courier;">Integer</span>类之前加载这个新的<span style="font-family: Courier;">Integer</span>实现。<br />
<br />
<strong><span style="font-size: 12pt;">4. verbose</span></strong><br />
&nbsp;&nbsp;&nbsp; 对于任何类型的Java应用，<span style="font-family: Courier;">-verbose</span>都是头等有用的诊断工具。该选项有三个子选项：<span style="font-family: Courier;">gc</span>，<span style="font-family: Courier;">class</span>和<span style="font-family: Courier;">jni</span>。<br />
&nbsp;&nbsp;&nbsp; 一般地，如果JVM垃圾收集器在运行并导致了低下的性能，那么gc就是开发员们第一次想要弄清楚的地方。不幸地是，解释gc的输出需要些技巧，这足够成为一整本书的主题了。更糟地是，在命令行窗口打印出的输出对于不同的Java发行版是不同的，或者对于不同的JVM也是不同的，这造成难以对其进行正确地解释。<br />
&nbsp;&nbsp;&nbsp; 一般来说，如果垃圾收集器是分代收集器(大多数"企业级"VM正是如此)，会有一些布尔型选项去指定是否使用完全清除；在Sun的JVM中，这样的选项会以<span style="font-family: Courier;">[Full GC ...]</span>的形式出现在GC输出行的开头。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">class</span>会是一个生命保护者，它尝试着诊断<span style="font-family: Courier;">ClassLoader</span>和/或不匹配的类冲突。它不仅会报告类是在何时被加载的，而且也会报告类是从何处被加载的，包括JAR文件的路径，如果这个类来自于一个JAR的话。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">jni</span>很少用到，除非你有使用JNI和原生库。当启用该选项时，它会报告各种JNI事件，例如原生库何时被加载的，以及方法何时被绑定的；另外，该报告输出会由于不同的Java版或JVM实现而不尽相同。<br />
<br />
<strong><span style="font-size: 12pt;">5. 命令行-X</span></strong><br />
&nbsp;&nbsp;&nbsp; 我已经列出了一些JVM提供的且我比较喜欢的命令行选项，但还有更多的选项你自己就能发现到。运行命令行参数-X列出所有JVM支持的非标准的(但多数就是安全的)参数--就像这些：<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">-Xint</span>，使JVM以解释模式进行运行(对于测试JIT编译器是否真的作用到你的程序，或是证明你是否有Bug在JIT编译器中，该选项是很用的)。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">-Xloggc:</span>，该参数所做的与<span style="font-family: Courier;">-verbose:gc</span>相同，但它会把日志记录到一个文件中，而不是一股脑地输出在命令窗口中。<br />
&nbsp;&nbsp;&nbsp; JVM命令行选项改了一次又一次，所以隔一段时间再去看会是一个好主意。这种区别就好像是，花上一整夜金城汤池盯着你的显示器，或者是下午五点就回家与爱人和孩子享用美餐(或是在Mass Effect 2中屠杀敌人，这取决于你的偏好)。<br />
<br />
<strong><span style="font-size: 12pt;">结论</span></strong><br />
&nbsp;&nbsp;&nbsp; 命令行选项的本意不是为了在产品环境中永久使用--事实上，除了你(可能)最终用于调优JVM垃圾收集器的选项之外，非标准的命令选项都不会被刻意地用到产品中。但作为能够窥探到完全不透明的虚拟机的内部工部情形的工具，它们是无价的。<br />
&nbsp;&nbsp;&nbsp; <a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=5+things+you+didn%27t+know+about">5 things系列</a>的下一篇文章：Java日常工具。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/330341.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-09-01 08:53 <a href="http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--JAR文件(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/06/27/324596.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 27 Jun 2010 10:01:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/06/27/324596.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/324596.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/06/27/324596.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/324596.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/324596.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">你所不知道的五件事情--JAR文件</span></strong></span><br />
</div>
<span style="font-size: 10pt;">
这是Ted Neward在<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中<a href="http://www.ibm.com/developerworks/java/library/j-5things6.html">5 things系列文章中的一篇</a>，讲述了关于JAR的一些应用窍门，值得大家学习。(2010.06.27最后更新)<br />
<br />
摘要：许多Java开发者从没有深入思考过JAR--他们只是在将类传到产品服务器之前使用JAR打包这些文件罢了。但JAR并不仅仅是一个被重命名的 ZIP文件。学习如何使用Java归档文件的全部能力，包括打包Spring依赖和配置文件的小窍门。<br />
<br />
&nbsp;&nbsp;&nbsp; 对大多数Java开发者而言，JAR与它的兄弟们，WAR和EAR，都是一长串Ant或Maven处理后的最终结果。一个标准的过程是将JAR复制到服务器的适应位置(或者，更少见地，复制到用户的机器上)，然后就把它遗忘了。<br />
&nbsp;&nbsp;&nbsp; 准确地说，JAR能做的远不止存储源代码，但你必须要知道它能做的其它事情，以及怎样去使用它。在本"5 things"系列的分期文章中所介绍的窍门将展示如何制作大部分的Java归档文件(在有些例子中，也会涉及WAR和EAR)，特别是在开发时期。<br />
&nbsp;&nbsp;&nbsp; 因为有众多的Java开发者在使用Spring(也因为Spring框架展示了一些相对于我们对JAR传统应用的挑战)，其中若干窍门是特别针对Spring应用中的JAR文件。<br />
&nbsp;&nbsp;&nbsp; 我将以一个标准的Java归档文件处理的例子开始，该例是下面各窍门的基础。<br />
<br />
<strong><span style="font-size: 12pt;">置于JAR中</span></strong><br />
&nbsp;&nbsp;&nbsp; 一般地，在编译源代码之后你会制作一个JAR文件，通过jar命令工具，或更为通用的Ant的jar工作，去把Java代码(已经被包分隔开)归集到单个文件中。这种处理很明了，在此处我就不作展示了，但在本文的后面我将回到JAR文件是如何被构造的这个主题中来。现在，我们只需打包Hello类，这是一个独立运行的控制台工具应用，该应用会向控制台打印一条信息，这无疑是很有用的任务，如清单1所示：<br />
<br />
<strong>清单1. 用于打包的控制台工具</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.tedneward.jars;<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;Hello<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)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Howdy!</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
Hello工具并不复杂，但以可执行程序开始，它是探索JAR文件的一个有用的辅助手段。 <br />
<br />
<strong><span style="font-size: 12pt;">1. JAR是可执行的</span></strong><br />
&nbsp;&nbsp;&nbsp; 像.NET和C++这样的编程语言，在历史上有操作系统友好方面优势，只需在命令行中引入它们的名字(helloWorld.exe)或在GUI Shell中双击它们的图标就会启动这些应用。然而在Java编程中，启动器程序--java--引导JVM运行，而后我们必须传入一个命令行参数 (com.tedneward.Hello)用于指定将要启动的含有main()方法的类。<br />
&nbsp;&nbsp;&nbsp; 这些额外的步骤使得很难为Java创建用户友好的应用。不仅仅是由于最终用户必须在命令行中键入所有的这些元素(很多用户都想避免这种情况)，而且他或她会由于某种原因打错字并得到一个晦涩的错误返回。<br />
&nbsp;&nbsp;&nbsp; 解决方案就是使JAR文件"可执行"，以便在执行JAR文件时，能让Java启动器自动地知道启动哪个类。我们所需要做的只是在JAR文件的manifest(JAR文件META-INF中的MENIFEST.MF文件)中引入一个属性，例如：<br />
<br />
<strong>清单2. 显示入口点</strong><br />
</span>
<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: #000000;">Main</span><span style="color: #000000;">-</span><span style="color: #000000;">Class:&nbsp;com.tedneward.jars.Hello</span></div>
<br />
<span style="font-size: 10pt;">manifest就是一组名值对。因为mainfest有时候对回车和空格比较敏感，所以在制作JAR时使用Ant去生成该文件要方便些。在清单3中，我就在Ant的jar任务中使用了manifest元素去指定要生成的manifest：<br />
<br />
<strong>清单3. 构建入口点</strong><br />
</span>
<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;">&lt;</span><span style="color: #800000;">target&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="jar"</span><span style="color: #ff0000;">&nbsp;depends</span><span style="color: #0000ff;">="build"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">jar&nbsp;</span><span style="color: #ff0000;">destfile</span><span style="color: #0000ff;">="outapp.jar"</span><span style="color: #ff0000;">&nbsp;basedir</span><span style="color: #0000ff;">="classes"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">manifest</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">attribute&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="Main-Class"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="com.tedneward.jars.Hello"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">manifest</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">jar</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">target</span><span style="color: #0000ff;">&gt;</span></div>
<span style="font-size: 10pt;"><br />
现在为了执行JAR文件，用户所需要做的只是在命令行中指定文件名，通过命令java -jar outapp.jar。对于这种情况，在有些GUI Shell中双击JAR文件也是可以的。<br />
<strong><span style="font-size: 12pt;"><br />
2. JAR能够包含依赖信息</span></strong><br />
&nbsp;&nbsp;&nbsp; Hello工具类的文字似乎已经扩展了，这样对不同的实现的需求就变得很紧急了。像Spring或Guice这样的依赖注入(DI)容器为我们处理了许多细节，但仍有一点儿障碍：<br />
<strong><br />
清单4. Hello, Spring world!</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.tedneward.jars;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.springframework.context.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.springframework.context.support.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<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;Hello<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)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationContext&nbsp;appContext&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FileSystemXmlApplicationContext(</span><span style="color: #000000;">"</span><span style="color: #000000;">./app.xml</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ISpeak&nbsp;speaker&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(ISpeak)&nbsp;appContext.getBean(</span><span style="color: #000000;">"</span><span style="color: #000000;">speaker</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(speaker.sayHello());<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
因为启动器的-jar选项会被命令行中的-classpath选项所覆盖，那么当你运行上述程序时，Spring需要出现在CLASSPATH中，并且要在环境变量中。幸运地是，JAR允许针对其它JAR依赖的声明出现在manfiest中，这就隐式地创建了CLASSPATH而不需要你去声明，如清单5 所示：<br />
<br />
<strong>清单5. Hello, Spring CLASSPATH!</strong><br />
</span>
<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;">&lt;</span><span style="color: #800000;">target&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="jar"</span><span style="color: #ff0000;">&nbsp;depends</span><span style="color: #0000ff;">="build"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">jar&nbsp;</span><span style="color: #ff0000;">destfile</span><span style="color: #0000ff;">="outapp.jar"</span><span style="color: #ff0000;">&nbsp;basedir</span><span style="color: #0000ff;">="classes"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">manifest</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">attribute&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="Main-Class"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="com.tedneward.jars.Hello"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">attribute&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="Class-Path"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value</span><span style="color: #0000ff;">="./lib/org.springframework.context-3.0.1.RELEASE-A.jar<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;./lib/org.springframework.core-3.0.1.RELEASE-A.jar<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;./lib/org.springframework.asm-3.0.1.RELEASE-A.jar<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;./lib/org.springframework.beans-3.0.1.RELEASE-A.jar<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;./lib/org.springframework.expression-3.0.1.RELEASE-A.jar<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;./lib/commons-logging-1.0.4.jar"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">manifest</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">jar</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">target</span><span style="color: #0000ff;">&gt;</span></div>
<br />
<span style="font-size: 10pt;">注意到Class-Path属性包含有该应用的依赖相对于JAR的引用路径。你也可以写绝对引用路径，或者完全不需要前缀，在这种情况下就要假设这些依赖 JAR文件与应用程序的JAR文件在同一目录下。<br />
&nbsp;&nbsp;&nbsp; 不幸地是，Ant的Class-Path属性对应的value属性必须出现在一行中，因为JAR manfiest无法应对多个Class-Path属性，所以，所有的依赖必须出现在manifest文件的同一行中。可以肯定的是，这种做法很丑陋，但为了能使用命令java -jar outapp.jar，这是值得的。<br />
<br />
<strong><span style="font-size: 12pt;">3. 可隐式地引用JAR</span></strong><br />
如果你有多个不同的命令行工具(或其它的应用)需要使用Spring框架的JAR文件，那么将这些Spring JAR文件置于公共路径中，以便所有的工具类都能被引用到。这样做就能避免文件系统中满是JAR文件的多份拷贝。Java运行时环境的公共JAR文件路径，即大家所知的"扩展目录"，默认是位于JRE安装路径下的lib/ext子目录中。<br />
JRE是一个可定制的路径，但仍然很少在一个给定的Java环境中定制该路径使我们能够安全放心地假设lib/ext是一个存放JAR文件的安全地方，该目录中的JAR文件将默认出现在Java运行时环境的CLASSPATH中。<br />
<strong><span style="font-size: 12pt;"><br />
4. Java 6允许类路通配符</span></strong><br />
&nbsp;&nbsp;&nbsp; 作为一种避免庞大CLASSPATH环境变量(Java开发者在多年前就已经抛弃它了)和/或命令行-classpath参数的努力，Java 6引入了类路径通配符选项。与在启动时必须在一个参数中显示地列出每个JAR文件不同，类路径通配符允许你通过lib/*来指定该目录下的所有JAR文件 (但不允许递归其子目录中的JAR文件)设置到类路径中。<br />
&nbsp;&nbsp;&nbsp; 不幸地是，类路径通配符并不能支持之前讨论过的Class-Path属性manifest条目。为了某些开发者任务，例如代码生成或分析工具，使用类路径通配符可以更方便地启动Java应用程序(包括服务器)。<br />
<strong><span style="font-size: 12pt;"><br />
5. JAR不只是包含代码</span></strong><br />
&nbsp;&nbsp;&nbsp; 就像Java生态系统中的许多组成部分那样，Spring依赖一个配置文件，该文件描述了如何去构建运行环境。如前所述，Spring依赖app.xml 文件，该文件与JAR文件存在于同一个目录下--但经常地，开发者们会忘记复制JAR文件边上的配置文件。<br />
&nbsp;&nbsp;&nbsp; sysadmin会编辑某些配置文件，但也有大量的配置文件(如Hibernate映射文件)在sysadmin的域之外，这将导致发布错误。一种明智的解决方案就是将代码与配置文件打包在一起--这是可行的，因为JAR本质上就是改头换面的ZIP。只需将配置文件包含在Ant任务中，或使用jar命令去构建JAR文件。<br />
&nbsp;&nbsp;&nbsp; 不仅仅是配置文件，JAR还可以包含其它类型的文件。例如，如果我的SpeakEnglish组件想到访问一个属性文件，那么我会像清单6那样进行设置：<br />
<br />
<strong>清单6. 随机响应</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.tedneward.jars;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<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;SpeakEnglish<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;ISpeak<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Properties&nbsp;responses&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Properties();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;random&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Random();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;sayHello()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Pick&nbsp;a&nbsp;response&nbsp;at&nbsp;random</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;which&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;random.nextInt(</span><span style="color: #000000;">5</span><span style="color: #000000;">);<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;responses.getProperty(</span><span style="color: #000000;">"</span><span style="color: #000000;">response.</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;which);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
将responses.properties置入JAR文件就意味着，不需要操心有太多文件要随JAR文件一同部署了。要做到这些，只需在制作JAR的过程中包含上responses.properties文件。<br />
一旦你在JAR中存放了配置文件，你就可能就想着如何得到它。如果你所想要的数据位于同一JAR文件中，可以让类的ClassLoader将该文件作为 JAR文件中的"资源"进行查找，使用ClassLoader的getResourceAsStream()方法，如清单7所示：<br />
<br />
<strong>清单7. ClassLoader定位资源</strong><br />
</span>
<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;">package</span><span style="color: #000000;">&nbsp;com.tedneward.jars;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<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;SpeakEnglish<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;ISpeak<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Properties&nbsp;responses&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Properties();<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #008000;"><br />
</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;SpeakEnglish()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassLoader&nbsp;myCL&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;SpeakEnglish.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">.getClassLoader();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;responses.load(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;myCL.getResourceAsStream(<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;">com/tedneward/jars/responses.properties</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;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(Exception&nbsp;x)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #008000;"><br />
</span><span style="color: #000000;">}</span></div>
<br />
<span style="font-size: 10pt;">你能使用这种操作找到任何类型的资源：配置文件，音频文件，图形文件，以及你所命名的其它文件。事实上，任何文件类型都可以绑定到JAR文件中，并通过 InputStream可再获得该文件(使用ClassLoader类)，然后就可以任何符合你喜好的方式来使用它们了。<br />
<br />
<strong><span style="font-size: 12pt;">结论</span></strong><br />
&nbsp;&nbsp;&nbsp; 本文涵盖了关于JAR的多数Java开发者最不知道的5件事情--至少基于历史和轶事证据可以这么认为。注意，所有这些与JAR相关的窍门也同样适用于 WAR。有些窍门(特别是Class-Path和Main-Class属性)对于WAR不完全正确，因为Servlet环境会获取目录中的全部内容并有一个预定义的入口点。但综合来看，这些窍门还是让我们超越了这样一种范式："好吧，让我们开始复制目录下的所有文件吧..."。除了这些，他们还使得部署 Java应用变得非常容易。<br />
&nbsp;&nbsp;&nbsp; 本系列的下一往篇文章是：你所不知道的五件事情--Java应用的性能监控。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/324596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-06-27 18:01 <a href="http://www.blogjava.net/jiangshachina/archive/2010/06/27/324596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Google Collections的理由(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/01/08/308638.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Fri, 08 Jan 2010 01:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/01/08/308638.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/308638.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/01/08/308638.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/308638.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/308638.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">使用Google Collections的理由</span></strong></span><br />
</div>
<span style="font-size: 10pt;"><a href="http://code.google.com/p/google-collections/">Google Collections</a>终于发布了1.0版，本文是<a href="http://java.dzone.com">JavaLobby</a>上的一篇<a href="http://java.dzone.com/articles/introduction-google">博文</a>，作者讲述了应该使用Google Collections的若干理由，大家有兴趣的话也可以体验一下。(2010.01.08最后更新)<br />
<br />
在去年底看到<a href="http://code.google.com/p/google-collections/">Google Collections</a>发布了它的1.0版之后，我想我应该看看与JDK默认提供的集合框架相比，使用Google Collections类库会有什么好处。Google Collections的发行包是一个<a href="http://google-collections.googlecode.com/files/google-collect-1.0.zip">zip文件</a>，包含了一个jar文件，源文件和Javadoc文件。<br />
Java Collections API就是被用来扩展的，而Google Collections则扩展的很好。很快，我就发现该类库最好的特性之一是它包含了<a href="http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/Multimap.html">MultiMap</a>类。<br />
为了证明这一点，让我们使用一个电话薄的例子，在这个例子中每个人都可一个或多个电话号码。事先，我已将电话号码存入每个人的一个单独的List中，如下所示：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Map</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Person,&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PhoneNumber</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;phoneBook&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;">Person,&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PhoneNumber</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">();</span></div>
<br />
<span style="font-size: 10pt;">
使用MultiMap，将变得十分简洁：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Multimap&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Person,&nbsp;PhoneNumber</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;newPhoneBook&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;ArrayListMultimap.create();</span></div>
<br />
<span style="font-size: 10pt;">
且在使用MultiMap时，就没必要管理每个人的电话号码列表了。当我要加一个新的电话号码时，只需这么做：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Person&nbsp;me&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Person(</span><span style="color: #000000;">"</span><span style="color: #000000;">James</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
newPhoneBook.put(me,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PhoneNumber(</span><span style="color: #000000;">111</span><span style="color: #000000;">));<br />
newPhoneBook.put(me,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PhoneNumber(</span><span style="color: #000000;">201</span><span style="color: #000000;">));</span></div>
<span style="font-size: 10pt;"><br />
而不是使用以前的繁冗方法去管理ArrayList：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Person&nbsp;me&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Person(</span><span style="color: #000000;">"</span><span style="color: #000000;">James</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(phoneBook.get(me)&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;phoneBook.put(me,&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;">PhoneNumber</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">());<br />
}</span></div>
<span style="font-size: 10pt;"><br />
phoneBook.get(me).add(new PhoneNumber(111));<br />
对需要处理的数据进行管理这个话题，<a href="http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Preconditions.html">Precondition</a>允许你对传入的参数进行校验。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Preconditions.checkNotNull(me);</span></div>
<br />
<span style="font-size: 10pt;">
我知道在Java中达到上述效果也可使用断言，但上述方法可使你的校验总是有效。你能方便地进行校验，通过判定一个表达式，也可为特定的参数抛出IllegalArgumentException。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Preconditions.checkArgument((i&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">),&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">The&nbsp;value&nbsp;i&nbsp;has&nbsp;to&nbsp;be&nbsp;greater&nbsp;than&nbsp;zero</span><span style="color: #000000;">"</span><span style="color: #000000;">);</span></div>
<br />
<span style="font-size: 10pt;">
还有许多其它有用的特性，例如快速创建不可变集合的能力：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">ImmutableList</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;ImmutableList.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;">5</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">8</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">13</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">21</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">34</span><span style="color: #000000;">);</span></div>
<br />
<span style="font-size: 10pt;">以前，你需要创建一个普通的List，然后再调用Collections.unmodifiableList()方法。<br />
为了对该类库有一个很好的概览，查看由Kevin Bourrillion在2008年给出的一段<a href="http://www.youtube.com/v/ZeO_J2OcHYM&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;">视频</a>。<br />
总之，该类库使你能以更优雅的方法来处理集合对象，并使你的代码更具可读性且更扼要。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/308638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-01-08 09:55 <a href="http://www.blogjava.net/jiangshachina/archive/2010/01/08/308638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在泛型中使用通配符和继承(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/12/28/307495.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 28 Dec 2009 05:44:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/12/28/307495.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/307495.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/12/28/307495.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/307495.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/307495.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">在泛型中使用通配符和继承</span></strong></span><br />
</div>
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">本文是Sun官方以Blog形式发布的Java核心技术窍门(<a href="http://blogs.sun.com/CoreJavaTechTips/">JavaCoreTechTip</a>)中的<a href="http://blogs.sun.com/CoreJavaTechTips/entry/using_generics_with_wildcards_and">一篇</a>，它以非常简洁的示例展示了泛型通配符的使用，初学Java泛型的朋友可以看看。(2009.12.30最后更新)<br />
&nbsp;&nbsp;&nbsp; Java2平台，标准版5.0(J2SE 5.0)为Java程序设计语言及其平台引入了泛型。在最简单的案例和典型的应用中，泛型能够识别集合容器中所存储的是否是你所期望的对象。所以，你可以特别说你有一个String或其它类型对象的List，而不是声称你的程序有一个Object的List。所以，如果你不小心向该List中加入了错误类型的对象，编译器会告之你这个错误。该错误将在编译时进行修复，而不用等到你运行该程序，且在程序运行到该处代码时，在获取对象的操作中产生一个运行时的强制类型转换异常。<br />
这就提出了泛型的第二个好处。迭代器将变得类型安全了。Iterator接口中的next()方法将会返回集合中下一个元素的类型安全版本。<br />
&nbsp;&nbsp;&nbsp; 但这并不是本文要介绍的泛型应用的窍门，那些窍门已由<a href="http://java.sun.com/developer/JDCTechTips/2005/tt0315.html#1">2005 Core Java Technologies Tip</a>描述过了。在使用泛型时，大多数人都不能很好地理解对extends关键字的使用。一个典型的描述如何使用<span style="font-family: Courier;">extends</span>关键字的示例与绘制图形有关。与其不同的是，此处窍门所用的示例将使用Swing组件，以便你不必创建额外的新类。在一个非常有限的例子中，Swing按钮组件的类层次结构如下所示，当然，Object是实际上的根。<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">Component<br />
</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;Container<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JComponent<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;AbstractButton<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JButton<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JMenuItem<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JCheckBoxMenuItem<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JMenu<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JRadioButtonMenuItem<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JToggleButton<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JCheckBox<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">&nbsp;JRadioButton</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 所有AbstractButton的子类都共同享有的一个东西就是方法getText。这就是泛型的精髓，你能定义一个方法去处理以AbstractButton为元素的List，并返回这些按钮的String类型的标签的List。下面是该方法的第一个版本：<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;getLabels(List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">AbstractButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;list)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;labelList&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;ArrayList</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">(list.size());<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">&nbsp;(AbstractButton&nbsp;button:&nbsp;list)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;labelList.add(button.getText());<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">&nbsp;labelList;<br />
}</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 下面就是如何使用该方法。首先，定义一个AbstractButton类型的List，然后向其中填充值，并调用该方法：<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">AbstractButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;buttonList&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;ArrayList</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">AbstractButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">();<br />
buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JButton(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Hello</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JCheckBox(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">World</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JRadioButton(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Hola</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JMenuItem(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Mundo</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
<br />
List&nbsp;labels&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;getLabels(buttonList);<br />
System.out.println(labels);</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 根据Google，"Hola, Mundo"是"Hello, World"的西班牙译文。调用println()方法的结果如下所示： <br />
[Hello, World, Hola, Mundo]<br />
<br />
&nbsp;&nbsp;&nbsp; 对于<span style="font-family: Courier;">AbstractButton</span>的<span style="font-family: Courier;">List</span>对象，一切都能正常运行，但当是其它类型，特别是AbstractButton子类型的List时，就不能正常工作了。从逻辑上，有人可能认为对于以JButton为元素的List，一切仍能正常工作。因为JButton是AbstractButton的子类。难道不能对AbstractButton子类型的List调用方法<span style="font-family: 宋体;">getLabels(List&lt;AbstractButton&gt;)</span>？<br />
然而，事实并非如此。因为这是一个编译时检查，同时也因为getLabels方法被定义为只接受AbstractButton的List，你不能向该方法中传入任何其它类型的List。<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">GetList.java:</span><span style="color: rgb(0, 0, 0);">13</span><span style="color: rgb(0, 0, 0);">:&nbsp;getLabels(java.util.List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">javax.swing.AbstractButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">)<br />
&nbsp;&nbsp;in&nbsp;GetList&nbsp;cannot&nbsp;be&nbsp;applied&nbsp;to&nbsp;(java.util.List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">javax.swing.JButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;labels&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;getLabels(buttonList);<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;</span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"><br />
</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">&nbsp;error</span></div>
<span style="font-size: 10pt;"><br />
这也就是extends关键字发挥作用的地方了。不将<span style="font-family: Courier;">getLabes</span>方法定义为仅仅接受<span style="font-family: Courier;">AbstractButton</span> <span style="font-family: Courier;">List</span>，而是将它定义为接受<span style="font-family: Courier;">AbstractButton</span>子类的<span style="font-family: Courier;">List</span>：<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;getLabels(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;?</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;AbstractButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;list)</span></div>
<span style="font-size: 10pt;"><br />
此处的通配符?表明该方法并不关心确切的类型是什么，只要它是AbstractButton的子类型即可。下面是综合了前述所有代码片断的完整示例程序：<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">;<br />
</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;javax.swing.</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">;<br />
<br />
</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;GetList&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;main(String&nbsp;args[])&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">JButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;buttonList&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;ArrayList</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">JButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JButton(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Hello</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JButton(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">World</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JButton(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Hola</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonList.add(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;JButton(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Mundo</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;labels&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;getLabels(buttonList);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(labels);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;getLabels(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;?</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;AbstractButton</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;list)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;labelList&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;ArrayList</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">(list.size());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">&nbsp;(AbstractButton&nbsp;button:&nbsp;list)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;labelList.add(button.getText());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">&nbsp;labelList;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
现在，当你要用泛型来定义你自己的类和方法时，就要考虑接受作为泛型参数的抽象类，或它的任一超类，记得使用通配符以便相同的方法对于子类也能很好地工作。<br />
更多关于泛型的信息，请见两篇较早前由Gilad Bracha撰写的教程：一篇是<a href="http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf">2004年的教程(PDF)</a>，另一篇是在线的<a href="http://java.sun.com/docs/books/tutorial/extra/generics/">Java Tutorial</a>中的泛型章节。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/307495.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2009-12-28 13:44 <a href="http://www.blogjava.net/jiangshachina/archive/2009/12/28/307495.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据加载模糊进度指示面板的实现与应用(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 29 Nov 2009 12:33:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/304120.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/304120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/304120.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">数据加载模糊进度指示面板的实现与应用</span></span></strong><br />
</div>
<span style="font-size: 10pt;">
当在加载数据(或其它耗时工作)时，需要显示一个进度指示面板，本文介绍了一种简易的实现方式。(2009.11.30最后更新)<br />
<br />
对于许多Swing应用，在与用户的交互过程中可能需要与数据库进行通信(如，加载数据)。而这个过程往往比较耗时，为了不造成"假死"现象，一般都会显示一个模糊进度指示器(不一定使用<a href="http://java.sun.com/javase/7/docs/api/javax/swing/JScrollBar.html">JProgressBar</a>，简单地用一个图片代替即可)，当数据加载完毕后，该进度指示器自动消失。<br />
&nbsp;&nbsp;&nbsp; 一般地，该模糊进度指示器不会展示在一个弹出的对话框中(因为这样不美观)，而是直接显示在需要展示被加载数据的面板中，并且对该面板进行模糊处理。实现这一功能的关键就在于，在屏幕的同一区域内展示两层面板：一层是展示数据的面板；另一层是展示进度指示器的面板。当加载数据时，显示进度指示器面板，并模糊数据面板；当数据加载完毕后，隐藏进度指示器面板，并使数据面板清晰显示。下面将使用<a href="http://swinglabs.org/hudson/job/SwingX%20Weekly%20Build/javadoc/org/jdesktop/swingx/StackLayout.html">org.jdesktop.swingx.StackLayout</a>方式来实现上述功能。<br />
<br />
</span><span style="font-size: 10pt;"><strong><span style="font-size: 12pt;">1. LoadingPanel--加载指示器面板</span></strong><br />
&nbsp;&nbsp;&nbsp; 首先创建一个加载指示器面板。如前所述，我们不必使用真正的进度条作为进度指示器，仅需要使用一张动态图片来代替即可。LoadingPanel的完整代码如下所示，</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;LoadingPanel&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JPanel&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1962748329465603630L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;mesg&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;LoadingPanel(String&nbsp;mesg)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.mesg&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;mesg;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interceptInput();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setOpaque(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setVisible(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;initUI()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JLabel&nbsp;label&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JLabel(mesg);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label.setHorizontalAlignment(JLabel.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label.setIcon(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ImageIcon(getClass().getResource(</span><span style="color: #000000;">"</span><span style="color: #000000;">/path/to/spinner.gif</span><span style="color: #000000;">"</span><span style="color: #000000;">)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setLayout(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add(label,&nbsp;BorderLayout.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;"> interceptInput()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseAdapter()&nbsp;{});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseMotionListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseMotionAdapter()&nbsp;{});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addKeyListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;KeyAdapter()&nbsp;{});<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addComponentListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ComponentAdapter()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;componentShown(ComponentEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestFocusInWindow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setFocusTraversalKeysEnabled(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">
上述代码很容易理解，LoadingPanel中仅有一个JLabel，它会展示一张图片(spinner.gif)及一段信息。但有两段代码需要特别说明：<br />
[1]构造器中的两行代码</span><br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">setVisible(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
setOpaque(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);</span><br />
</div>
<span style="font-size: 10pt;">
LoadingPanel只在加载数据时才显示，其它时候是不显示的，所以它默认不可见。另外，在显示LoadingPanel的同时，我们仍然希望能看到数据面板，所以LoadingPanel应该是透明的。<br />
[2]interceptInput方法<br />
当LoadingPanel显示之后，我们不希望用户还能够操作数据面板，那么就需要屏蔽掉用户(鼠标，键盘)输入。<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">addMouseListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseAdapter()&nbsp;{});<br />
addMouseMotionListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseMotionAdapter()&nbsp;{});<br />
addKeyListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;KeyAdapter()&nbsp;{});</span></div>
<span style="font-size: 10pt;">上述三行代码就使得LoadingPanel能捕获所有的鼠标与键盘事件，并忽略掉它们。但仅仅如此还不够，在展示LoadingPanel时，数据面板中的某个UI组件很可能已经获得焦点了，那么用户仍然可以通过键盘操控数据面板中的组件(因为系统会把键盘事件发送给当前获取焦点的组件)。而且，即使数据面板中没有任何组件获得焦点，用户仍然可以通过Tab键把焦点转移到数据面板中的组件上。为了阻止这一操作，还需要加上如下几行代码，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">addComponentListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ComponentAdapter()&nbsp;{&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;一旦LoadingPanel可见，即获取焦点</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;componentShown(ComponentEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestFocusInWindow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
});<br />
setFocusTraversalKeysEnabled(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;阻止用户转移焦点</span></div>
<span style="font-size: 10pt;"><strong><span style="font-size: 12pt;"><br />
2. 示例程序</span></strong><br />
&nbsp;&nbsp;&nbsp; 在此处的示例程序中，数据面板(dataPanel)中仅有一个按钮，当点击该按钮时会显示loadingPanel，且模糊掉dataPanel，并会启动一个新的线程，该线程会在睡眠大约3秒(模拟耗时的数据加载工作)之后隐藏loadingPanel，且使dataPanel重新清晰可见。<br />
&nbsp;&nbsp;&nbsp; 值得注意的是，该示例程序使用了<a href="https://swingx.dev.java.net/">SwingX</a>中的两个组件：<a href="http://download.java.net/javadesktop/swinglabs/releases/0.8/docs/api/org/jdesktop/swingx/JXPanel.html">JXPanel</a>和<a href="http://swinglabs.org/hudson/job/SwingX%20Weekly%20Build/javadoc/org/jdesktop/swingx/StackLayout.html">StackLayout</a>。JXPanel提供了一个方法(setAlpha)以方便地设置Panel的透明度(Alpha值)；而StackLayout允许在同一块区域内添加多层组件，并能同时展示所有层的组件(而，<a href="http://java.sun.com/javase/7/docs/api/java/awt/GridLayout.html">CardLayout</a>一次只能显示某一层的组件)。完整的示例程序如下所示，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;LoadDataDemo&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JFrame&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">5927602404779391420L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;JXPanel&nbsp;dataPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;使用org.jdesktop.swingx.JXPanel，以方便设置清晰度</span><span style="color: #008000;"><br />
</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;LoadingPanel&nbsp;loadingPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;LoadDataDemo()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">LoadData&nbsp;Demo</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initUI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;initUI()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JButton&nbsp;button&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JButton(</span><span style="color: #000000;">"</span><span style="color: #000000;">Load&nbsp;Data</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;button.addActionListener(handler);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JXPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FlowLayout(FlowLayout.CENTER));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel.add(button);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadingPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;LoadingPanel(</span><span style="color: #000000;">"</span><span style="color: #000000;">Loading<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;使用org.jdesktop.swingx.StackLayout，将loadingPanel置于dataPanel的上方</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JPanel&nbsp;centerPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;JXPanel(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;StackLayout());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;centerPanel.add(dataPanel,&nbsp;StackLayout.TOP);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;centerPanel.add(loadingPanel,&nbsp;StackLayout.TOP);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Container&nbsp;container&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getContentPane();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.setLayout(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BorderLayout());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(centerPanel,&nbsp;BorderLayout.CENTER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">transient</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;ActionListener&nbsp;handler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ActionListener()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;将dataPanel及其子组件的清晰度设置为50%；并显示loadingPanel</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel.setAlpha(</span><span style="color: #000000;">0.5F</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadingPanel.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;thread&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Thread()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span style="color: #000000;">3000L</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;睡眠约3秒钟，以模拟加载数据的过程</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;数据加载完毕后，重新隐藏loadingPanel；并使dataPanel及其子组件重新清晰可见</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadingPanel.setVisible(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataPanel.setAlpha(1F);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thread.start();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoadDataDemo&nbsp;demo&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;LoadDataDemo();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.setSize(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dimension(</span><span style="color: #000000;">400</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">300</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><strong><span style="font-size: 12pt;"><br />
3. 不使用SwingX</span></strong><br />
&nbsp;&nbsp;&nbsp; SwingX为我们提供了一系列功能强大，使用简易的Swing扩展组件，我强烈建议你去使用它。但若因故，你不准备使用它时，我们仍然有替代的解决方案，但此处仅简述一二。<br />
[1]对于设置Alpha值，需要创建一个继承自<a href="http://java.sun.com/javase/7/docs/api/javax/swing/JPanel.html">JPanel</a>的DataPanel类，覆写paintComponent方法，在其中使用Alpha合成，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">Graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,&nbsp;alpha));</span></div>
<span style="font-size: 10pt;">[2]对于StackLayout，我们可以使用GlassPane(玻璃窗格)或LayeredPane(分层窗格)进行替换，将LoadingPanel设置为GlassPane或LayeredPanel中的一层。由于一个JFrame只有一个GlassPane，为了程序的灵活性，一般首选使用LayeredPane。<br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/304120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2009-11-29 20:33 <a href="http://www.blogjava.net/jiangshachina/archive/2009/11/29/304120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>判定一个点是否在三角形内(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/07/24/217214.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 24 Jul 2008 09:02:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/07/24/217214.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/217214.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/07/24/217214.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/217214.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/217214.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">判定一个点是否在三角形内</span></span></strong><br />
</div>
<span style="font-size: 10pt;">如何判定一个点P是否存在于指定的三角形ABC内，这肯定是一个简单的问题，本文仅用一个图形界面程序展示了该问题，有兴趣的朋友可以看看。(2008.07.24最后更新)<br />
<br />
在此处使用一种常见且简便的方法：<strong>如果三角形PAB，PAC和PBC的面积之和与三角形ABC的面积相等，即可判定点P在三角形ABC内(包括在三条边上)</strong>。<br />
可知，该方法的关键在于如何计算三角形的面积。幸运地是，当知道三角形顶点(A，B和C)的坐标((Ax, Ay)，(Bx, By)和(Cx, Cy))之后，即可计算出其面积：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">S&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;"> |(Ax&nbsp;*&nbsp;By&nbsp;+&nbsp;Bx&nbsp;*&nbsp;Cy&nbsp;+&nbsp;Cx&nbsp;*&nbsp;Zy&nbsp;-&nbsp;Ay&nbsp;*&nbsp;Bx&nbsp;-&nbsp;By&nbsp;*&nbsp;Cx&nbsp;-&nbsp;Cy&nbsp;*&nbsp;Ax)&nbsp;/&nbsp;</span><span style="color: #000000;">2|</span></div>
<span style="font-size: 10pt;"><br />
关键的代码如下，<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;由给定的三个顶点的坐标，计算三角形面积。<br />
</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Point(java.awt.Point)代表点的坐标。</span><span style="color: #008000;"><br />
</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;">double</span><span style="color: #000000;">&nbsp;triangleArea(Point&nbsp;pos1,&nbsp;Point&nbsp;pos2,&nbsp;Point&nbsp;pos3)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Math.abs((pos1.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos2.y&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;pos2.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos3.y&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;pos3.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos1.y<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;pos2.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos1.y&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;pos3.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos2.y&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;pos1.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos3.y)&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2.0D</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br />
}</span></div>
<span style="font-size: 10pt;"><br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;判断点pos是否在指定的三角形内。</span><span style="color: #008000;"><br />
</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;">boolean</span><span style="color: #000000;">&nbsp;inTriangle(Point&nbsp;pos,&nbsp;Point&nbsp;posA,&nbsp;Point&nbsp;posB,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;posC)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;triangleArea&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;triangleArea(posA,&nbsp;posB,&nbsp;posC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;area&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;triangleArea(pos,&nbsp;posA,&nbsp;posB);<br />
&nbsp;&nbsp;&nbsp;&nbsp;area&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;triangleArea(pos,&nbsp;posA,&nbsp;posC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;area&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;triangleArea(pos,&nbsp;posB,&nbsp;posC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;epsilon&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0.0001</span><span style="color: #000000;">;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;由于浮点数的计算存在着误差，故指定一个足够小的数，用于判定两个面积是否(近似)相等。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(Math.abs(triangleArea&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;area)&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;epsilon)&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;">true</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
}</span></div>
<span style="font-size: 10pt;"><br />
执行该应用程序，用鼠标在其中点击三次，即可绘制一个三角形，如下组图所示：<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/t01.PNG" height="200" width="300" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/t02.PNG" height="200" width="300" /><br />
然后仅需移动鼠标，就会出现一个空心圆圈。如果圆圈的中心在三角内(包含在三条边上)，则圆圈显示为红色；否则，显示为蓝色。如下组图所示：<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/t03.PNG" height="200" width="300" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/t04.PNG" height="200" width="300" /> <img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/t05.PNG" height="200" width="300" /><br />
<br />
完整代码如下：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;CanvasPanel&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;JPanel&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">6665936180725885346L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Point&nbsp;firstPoint&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;Point&nbsp;secondPoint&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;Point&nbsp;thirdPoint&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;CanvasPanel()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setBackground(Color.WHITE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseListener(mouseAdapter);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addMouseMotionListener(mouseAdapter);<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;paintComponent(Graphics&nbsp;g)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.paintComponent(g);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawTriangel(g);<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;drawTriangel(Graphics&nbsp;g)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(firstPoint&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;secondPoint&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;g.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;secondPoint.x, secondPoint.y);<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;(thirdPoint&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;g.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;thirdPoint.x, thirdPoint.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(secondPoint.x,&nbsp;secondPoint.y,&nbsp;thirdPoint.x, thirdPoint.y);<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;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;inTriangle(Point&nbsp;pos,&nbsp;Point&nbsp;posA,&nbsp;Point&nbsp;posB,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;posC)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;triangeArea&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;triangleArea(posA,&nbsp;posB,&nbsp;posC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;area&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;triangleArea(pos,&nbsp;posA,&nbsp;posB);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;area&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;triangleArea(pos,&nbsp;posA,&nbsp;posC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;area&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;triangleArea(pos,&nbsp;posB,&nbsp;posC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;epsilon&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0.0001</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;(Math.abs(triangeArea&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;area)&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;epsilon)&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;">&nbsp;</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;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;triangleArea(Point&nbsp;pos1,&nbsp;Point&nbsp;pos2,&nbsp;Point&nbsp;pos3)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Math.abs((pos1.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos2.y&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;pos2.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos3.y&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;pos3.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos1.y<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; </span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;pos2.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos1.y&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;pos3.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos2.y&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;pos1.x&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pos3.y)&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2.0D</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;MouseInputAdapter&nbsp;mouseAdapter&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MouseInputAdapter()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;mouseReleased(MouseEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;pos&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.getPoint();<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;(firstPoint&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;firstPoint&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;pos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(secondPoint&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;secondPoint&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;pos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Graphics&nbsp;g&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;CanvasPanel.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.getGraphics();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CanvasPanel.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.paintComponent(g);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;secondPoint.x, secondPoint.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(thirdPoint&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;thirdPoint&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;pos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Graphics&nbsp;g&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;CanvasPanel.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.getGraphics();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CanvasPanel.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.paintComponent(g);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;secondPoint.x, secondPoint.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;thirdPoint.x, thirdPoint.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(secondPoint.x,&nbsp;secondPoint.y,&nbsp;thirdPoint.x, thirdPoint.y);<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;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;mouseMoved(MouseEvent&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point&nbsp;pos&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.getPoint();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Graphics2D&nbsp;g2&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Graphics2D)&nbsp;CanvasPanel.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.getGraphics();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CanvasPanel.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.paintComponent(g2);<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;(firstPoint&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;secondPoint&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;g2.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;pos.x,&nbsp;pos.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(firstPoint&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;secondPoint&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;thirdPoint&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;g2.drawLine(firstPoint.x,&nbsp;firstPoint.y,&nbsp;pos.x,&nbsp;pos.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g2.drawLine(secondPoint.x,&nbsp;secondPoint.y,&nbsp;pos.x,&nbsp;pos.y);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(firstPoint&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;secondPoint&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;"> </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;thirdPoint&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;">if</span><span style="color: #000000;">&nbsp;(inTriangle(pos,&nbsp;firstPoint,&nbsp;secondPoint,&nbsp;thirdPoint))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g2.setColor(Color.RED);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g2.setColor(Color.BLUE);<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;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;radius&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g2.drawOval(pos.x&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;radius,&nbsp;pos.y&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;radius,&nbsp;radius&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">, radius&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;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
}<br />
</span></div>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Triangle&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;">1L</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;CanvasPanel&nbsp;mainPanel&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;Triangle()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setTitle(</span><span style="color: #000000;">"</span><span style="color: #000000;">Triangle</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setSize(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dimension(</span><span style="color: #000000;">300</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">200</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setResizable(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;init();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Container&nbsp;container&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getContentPane();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;container.add(mainPanel);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;init()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mainPanel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;CanvasPanel();<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;">new</span><span style="color: #000000;">&nbsp;Triangle();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span></div>
<span style="font-size: 10pt;"><br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/217214.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2008-07-24 17:02 <a href="http://www.blogjava.net/jiangshachina/archive/2008/07/24/217214.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Callable返回结果(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/05/31/204007.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 31 May 2008 14:24:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/05/31/204007.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/204007.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/05/31/204007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/204007.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/204007.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">使用Callable返回结果</span></span></strong><br />
</div>
<span style="font-size: 10pt;">
&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt;">本文是Sun官方以Blog形式发布的Java核心技术窍门(<a href="http://blogs.sun.com/CoreJavaTechTips/">JavaCoreTechTip</a>)中的一个。本文主要介绍了Callable及其相关接口和类的使用，篇幅不长且易于理解，故翻译在了此处，相信对于准备或刚接触java.util.concurrent的朋友会有所帮助。(2008.05.31最后更新)<br />
<br />
</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 自从Java平台的最开始，Runnable接口就已存在了。它允许你定义一个可由线程完成的任务。如大多数人所已知的那样，它只提供了一个run方法，该方法既不接受任何参数，也不返回任何值。如果你需要从一个未完成的任务中返回一个值，你就必须在该接口之外使用一个方法去等待该任务完成时通报的某种消息。例如，下面的示例就是你在这种情景下可能做的事情：<br />
&nbsp;&nbsp;&nbsp; Runnable runnable = ...;<br />
&nbsp;&nbsp;&nbsp; Thread t = new Thread(runnable);<br />
&nbsp;&nbsp;&nbsp; t.start();<br />
&nbsp;&nbsp;&nbsp; t.join();<br />
&nbsp;&nbsp;&nbsp; String value = someMethodtoGetSavedValue()<br />
严格来说，上述代码并无错误，但现在可用不同的方法去做，这要感谢J2SE 5.0引入的Callable接口。不同于Runnable接口拥有run方法，Callable接口提供的是call方法，该方法可以返回一个Object对象，或可返回任何一个在泛型化格式中定义了的特定类型的对象。<br />
&nbsp;&nbsp;&nbsp; public interface Callable&lt;V&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V call() throws Exception;<br />
&nbsp;&nbsp;&nbsp; }<br />
因为你不可能把Callable对象传到Thread对象去执行，你可换用ExecutorService对象去执行Callable对象。该服务接受Callable对象，并经由submit方法去执行它。<br />
&nbsp;&nbsp;&nbsp; &lt;T&gt; Future&lt;T&gt; submit(Callable&lt;T&gt; task)<br />
如该方法的定义所示，提交一个Callable对象给ExecutorService会返回一个Future对象。然后，Future的get方法将会阻塞，直到任务完成。<br />
&nbsp;&nbsp;&nbsp; 为了证明这一点，下面的例子为命令行中的每个词都创建一个单独的Callable实例，然后把这些词的长度加起来。各个Callable对象将只是计算它自己的词的长度之和。Futures对象的Set集合将被保存以便从中获得计算用的值。如果需要保持返回值的顺序，则可换用一个List对象。</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; ">import</span><span style="color: #000000; ">&nbsp;java.util.</span><span style="color: #000000; ">*</span><span style="color: #000000; ">;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.util.concurrent.</span><span style="color: #000000; ">*</span><span style="color: #000000; ">;<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;CallableExample&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; ">class</span><span style="color: #000000; ">&nbsp;WordLengthCallable<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Callable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;String&nbsp;word;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;WordLengthCallable(String&nbsp;word)&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; ">.word&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;word;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Integer&nbsp;call()&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; ">&nbsp;Integer.valueOf(word.length());<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;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExecutorService&nbsp;pool&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Executors.newFixedThreadPool(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;set&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;HashSet</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Future</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">lg;Integer</span><span style="color: #000000; ">&gt;&gt;</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;word:&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Callable</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;callable&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;WordLengthCallable(word);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;future&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;pool.submit(callable);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set.add(future);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><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 />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(Future</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;future&nbsp;:&nbsp;set)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sum&nbsp;</span><span style="color: #000000; ">+=</span><span style="color: #000000; ">&nbsp;future.get();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">The&nbsp;sum&nbsp;of&nbsp;lengths&nbsp;is&nbsp;%s%n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;sum);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(sum);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">
WordLengthCallable保存了每个词并使用该词的长度作为call方法的返回值。这个值可能会花点儿时间去生成，不过在这个例子中，可以立即知道它。 call方法的唯一要求是这个值要在call方法的结尾处返回。当Future的get方法稍后被调用时，如果任务运行得很快的话，Future将会自动得到这个值(如同本例的情况)，否则将一直等到该值生成完毕为止。多次调用get方法不会导致任务从该线程返回。因为该程序的目的是计划所有字的长度之和，它不会强令Callable任务结束。如果最后一个任务在前三个任务之前完成，也是没错的。对Future的get方法的第一次调用将只会等待Set中第一个任务结束，而不会阻塞其它的任务分别执行完毕。它只会等待当次线程或任务结束。这个特定的例子使用固定数线程池来产生ExecutorService对象，但其它有效的方法也是可行的。<br />
&nbsp;&nbsp;&nbsp; 关于执行器和线程池用法的更多信息，请见Java Tutorial中<a href="http://java.sun.com/tutorial/essential/concurrency/executors.html">Executors</a>一节。SwingWorker类是另一个使用Future的Runnable对象的例子，尽管有些微不同之处。更多信息请见Java Tutorial中<a href="http://java.sun.com/docs/books/tutorial/uiswing/concurrency/worker.html">Worker Threads and SwingWorker</a>一节。<br />
<br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/204007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2008-05-31 22:24 <a href="http://www.blogjava.net/jiangshachina/archive/2008/05/31/204007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>