﻿<?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-随笔分类-翻译</title><link>http://www.blogjava.net/jiangshachina/category/32068.html</link><description>同是Java爱好者，相逢何必曾相识！&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;a cup of Java, cheers!</description><language>zh-cn</language><lastBuildDate>Sun, 02 Nov 2008 11:14:47 GMT</lastBuildDate><pubDate>Sun, 02 Nov 2008 11:14:47 GMT</pubDate><ttl>60</ttl><item><title>更好的Applet体验：海报帧(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/11/02/237466.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 02 Nov 2008 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/11/02/237466.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/237466.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/11/02/237466.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/237466.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/237466.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong style="font-size: 14pt;">更好的Applet体验：海报帧</strong></span><br />
</div>
<span style="font-size: 10pt;">
本文是</span><a href="http://weblogs.java.net/blog/joshy"><span style="font-size: 10pt;">Joshua Marinacci</span></a><span style="font-size: 10pt;">的系列博客<em>A Better Applet Experience</em>中的</span><a href="http://weblogs.java.net/blog/joshy/archive/2008/08/a_better_applet_1.html"><span style="font-size: 10pt;">第二篇</span></a><span style="font-size: 10pt;">，介绍了如何使用海报帧去加快页面的加载。(2008.11.02最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 在本系列文章的第一部分中，我向你展示了如何使用设置加载时图片，包括动态的GIF自旋体图片。这次，我将向展示如何使用屏幕截图或海报帧去加快页面的加载。<br />
&nbsp;&nbsp;&nbsp; Quicktime电影就有海报帧的思想。电影中的一帧(经常就是其中的第一帧)将会放到该电影出现的地方。当用户点击海报帧时，实现的电影才会通过网络被加载进来。这就能极大地加快该电影所在Web页面的加载速度。使用少许JavaScript，完全可以为Java Applet实现相同的功能。<br />
<br />
<strong><span style="font-size: 12pt;"><strong>Applet的海报帧</strong><br />
</span></strong>&nbsp;&nbsp;&nbsp; <strong>在研究这个Demo之前，我必须要提到，我个人尚未在IE浏览器中测试过这个Demo。如果你发现这个Demo不能在某个浏览器中运行，请向错误信息发送给我，以便我能更新这个JavaScript。</strong><br />
&nbsp;&nbsp;&nbsp; 基本思想非常的简单，创建一个div，它包含一个链接和一张屏幕截图。当用户点击该链接时，我们使用一个新的<code>applet</code>元素去替换页面中的<code>a</code>和<code>img</code>元素。一旦浏览器察觉到了这个新的<code>applet</code>元素，它将下载Java插件并启动这个Applet。<br />
&nbsp;&nbsp;&nbsp; 这就有一个例子：如果你点击了这张图片，一个Applet就会被加载到该图片所在的位置，并会说"applet loaded"。<br />
<br />
<strong style="font-size: 12pt;">JavaScript</strong><br />
&nbsp;&nbsp;&nbsp; 如下就是这个JavaScript脚本：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="font-size: 10pt;"><span style="color: #000000;">&lt;</span><span style="color: #000000;">script</span><span style="color: #000000;">&gt;</span></span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;"><span style="font-size: 10pt;">function</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;generateInlineAppletTag(appletID,&nbsp;screenshotID)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;attributes&nbsp;</span><span style="color: #000000;">=</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;code:'animatedstartup.MainApplet',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width:</span><span style="color: #000000;">100</span></span><span style="font-size: 10pt;"><span style="color: #000000;">,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height:</span><span style="color: #000000;">100</span></span><span style="font-size: 10pt;"><span style="color: #000000;">,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;archive:'http:</span><span style="color: #008000;">//</span><span style="color: #008000;">projects.joshy.org/demos/AnimatedStartup/AnimatedStartup.jar',</span></span><span style="color: #008000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:'fooApplet'<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;parameters&nbsp;</span><span style="color: #000000;">=</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image:'http:</span><span style="color: #008000;">//</span><span style="color: #008000;">projects.joshy.org/demos/AnimatedStartup.gif',</span></span><span style="color: #008000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;centerImage:'</span><span style="color: #0000ff;">true</span></span><span style="font-size: 10pt;"><span style="color: #000000;">'<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;appletTag&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;document.createElement(</span><span style="color: #000000;">"</span><span style="color: #000000;">applet</span><span style="color: #000000;">"</span></span><span style="font-size: 10pt;"><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;attribute&nbsp;</span><span style="color: #0000ff;">in</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;attributes)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appletTag.setAttribute(attribute,attributes[attribute]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(parameters&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;'#ff0000'&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;parameters&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span></span><span style="font-size: 10pt;"><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;parameter&nbsp;</span><span style="color: #0000ff;">in</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;parameters)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;param&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;document.createElement(</span><span style="color: #000000;">"</span><span style="color: #000000;">PARAM</span><span style="color: #000000;">"</span></span><span style="font-size: 10pt;"><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param.setAttribute(</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span></span><span style="font-size: 10pt;"><span style="color: #000000;">,parameter);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param.setAttribute(</span><span style="color: #000000;">"</span><span style="color: #000000;">value</span><span style="color: #000000;">"</span></span><span style="font-size: 10pt;"><span style="color: #000000;">,parameters[parameter]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appletTag.appendChild(param);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;bodyRef&nbsp;</span><span style="color: #000000;">=</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;document.getElementById(appletID);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;screenshot&nbsp;</span><span style="color: #000000;">=</span></span><span style="font-size: 10pt;"><span style="color: #000000;">&nbsp;document.getElementById(screenshotID);<br />
&nbsp;&nbsp;&nbsp;&nbsp;bodyRef.removeChild(screenshot);<br />
&nbsp;&nbsp;&nbsp;&nbsp;bodyRef.appendChild(appletTag);<br />
}<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">script</span><span style="color: #000000;">&gt;</span></span></div>
<span style="font-size: 10pt;">该脚本的第一部分初始化了两个哈希表，它们包含了<code>applet</code>元素的属性与嵌套的param标签。然后，它会使用<code>document.createElement()</code>方法去创建这个applet，并配置其中的属性与<code>PARAM</code>元素。到目前为止，还很直接。真正地奇妙之事发生在<code>generateInlineAppletTag</code>函数的最后四行。它使用传入的<code>appletID</code>参数与包含着上述链接与图片的<code>div</code>元素相联在一起，然后它会找到并删除该链接，而用新的applet元素去替代这个链接。<br />
<br />
<strong style="font-size: 12pt;">HTML</strong><br />
要使用上述javascript函数，你只需将它放到页面的顶部，并通过那张屏幕截图链接的href去调用它。在本例中，我使用了：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">div&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="appletDiv"</span><span style="color: #0000ff;">&gt;</span></span><span style="color: #000000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">a&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="screenshot1"</span><span style="color: #ff0000;">&nbsp;href</span><span style="color: #0000ff;">="javascript:generateInlineAppletTag('appletDiv','screenshot1');"</span><span style="color: #0000ff;">&gt;</span></span><span style="color: #000000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">img&nbsp;</span><span style="color: #ff0000;">src</span><span style="color: #0000ff;">="http://projects.joshy.org/demos/AnimatedStartup/applet_screenshot.png"</span><span style="color: #ff0000;">&nbsp;border</span><span style="color: #0000ff;">="0"</span></span><span style="color: #ff0000;"><br />
</span><span style="color: #0000ff;"><span style="font-size: 10pt;">/&gt;</span></span><span style="color: #000000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">a</span><span style="color: #0000ff;">&gt;</span></span><span style="color: #000000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">div</span><span style="color: #0000ff;">&gt;</span></span></div>
<span style="font-size: 10pt;">这个div被命名为<code>appletDiv</code>，链接被命名为<code>screenshot1</code>。该链接的<code>href</code>会调用上述javascript函数，并传入div与链接的名称。最后该图片就只是显示将要运行的Applet的一张屏幕截图，该图被一个播放按钮覆盖着。就这么简单，在</span><a href="http://projects.joshy.org/demos/AppletOverlay/image_replace.html"><span style="font-size: 10pt;">这里</span></a><span style="font-size: 10pt;">你可以看到完整的javascript和HTML语句。<br />
<br />
<strong style="font-size: 12pt;">结论</strong><br />
这个javascript只是一个开始。你还能很容易的扩展它，使图片上能展现关于这个Applet的更多信息。你也可以把这个javascript重写为一个更加可重用的形式，使它能够被你的整个站点所共享。<br />
下次我将向你展示如何使用新的发布工具包去侦测当前已安装的Java版本，并启动更新程序。<br />
<strong>重复一遍，如果你在某个平台或浏览器中发现了任何错误，请让我知道，以便更新这个脚本。</strong><br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/237466.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-11-02 10:57 <a href="http://www.blogjava.net/jiangshachina/archive/2008/11/02/237466.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>更好的Applet体验：定制加载时画面(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/29/236954.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 28 Oct 2008 23:32:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/29/236954.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/236954.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/29/236954.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/236954.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/236954.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;"><strong>更好的Applet体验：定制加载时画面</strong></span><br />
</span></strong></div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文是</span><a href="http://weblogs.java.net/blog/joshy"><span style="font-size: 10pt;">Joshua Marinacci</span></a><span style="font-size: 10pt;">的系列博客<em>A Better Applet Experience</em>中的</span><a href="http://weblogs.java.net/blog/joshy/archive/2008/08/a_better_applet.html"><span style="font-size: 10pt;">第一篇</span></a><span style="font-size: 10pt;">，介绍了如何定制加载Applet时使用的画面。(2008.10.30最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 你可能已经听说了Java SE 6 Update 10应当会改进Applet体验。你可能已经看过一些可在浏览器中拖拽的Applet示例，但还有比那更多的东西。在本博客系列中，我将向你展示如何在发布Applet时获得尽量多的东西，纵使你不使用Update 10。<br />
<br />
<strong><span style="font-size: 12pt;"><strong>摆脱咖啡杯图标</strong><br />
</span></strong>&nbsp;&nbsp;&nbsp; 很多人抱怨的第一件事情就是加载Java Applet时使用的咖啡杯图标。根据你具体使用的OS和JVM版本，这个图标是不同的，但它看起来总是像下图那样。<br />
<img alt="" src="http://weblogs.java.net/blog/campbell/archive/images/orangebox.png" /> <br />
&nbsp;&nbsp;&nbsp; 有很多理由可以认为这会困扰到应用程序开发者，包括不能为他们自己的软件标注品牌，破坏应用的视觉观感，滥用橘黄色。这些都是正确的批评。但在这儿我要告诉你，无须遵循这种方式。这个橘黄色的图标只是一种默认的方式罢了。你轻易就能，也应该将加载画面所使用的图片改成与你网站的其它部分相适应的图片。<br />
&nbsp;&nbsp;&nbsp; 在Java SE 6 Update 10出现的很长时间之前，Applet插件就已经支持在加载画面中使用定制图片了。只需设置Applet的<em>image</em>参数就可以了。就是这么简单!这儿就有一个例子：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">applet&nbsp;</span><span style="color: #ff0000;">code</span><span style="color: #0000ff;">="animatedstartup.MainApplet"</span></span><span style="color: #ff0000;"><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;archive</span></span><span style="color: #0000ff;"><span style="font-size: 10pt;">="AnimatedStartup.jar"</span></span><span style="color: #ff0000;"><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width</span></span><span style="font-size: 10pt;"><span style="color: #0000ff;">="100"</span><span style="color: #ff0000;">&nbsp;height</span><span style="color: #0000ff;">="100"</span></span><span style="color: #ff0000;"><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="color: #0000ff;"><span style="font-size: 10pt;">&gt;</span></span><span style="color: #000000;"><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">PARAM&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="image"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="earth.gif"</span><span style="color: #0000ff;">/&gt;</span></span><span style="color: #000000;"><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">PARAM&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="boxmessage"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="loading<img alt="" src="http://www.blogjava.net/Images/dot.gif" />"</span><span style="color: #0000ff;">/&gt;</span></span><span style="color: #000000;"><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">PARAM&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="boxborder"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="false"</span><span style="color: #0000ff;">/&gt;</span></span><span style="color: #000000;"><br />
</span><span style="font-size: 10pt;"><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">applet</span><span style="color: #0000ff;">&gt;</span></span></div>
<span style="font-size: 10pt;"><em>image</em>参数将设置一个加载时图片。Update 10还支持添加加载时信息，以及不绘制Applet的边框，但<em>image</em>是比较重要的一个参数。上面这个例子看起来就像下面这样：<br />
屏幕截图<sup>[1]</sup><br />
<br />
<strong style="font-size: 12pt;">制作加载时动画</strong><br />
&nbsp;&nbsp;&nbsp; Java 6目前支持使用动态图片以添加加载时动画。在前面例子中使用的earth.gif图片文件就正是一个动态GIF文件。如果你有最新的Applet插件，就会看到加载时画面是动态的。当然，你可能想要一个自旋体的图片，它更合适些，就如你在这里看到的那样：你也可以使用一个更具有指示性的GIF加载图片，就像</span><a href="http://www.command-tab.com/2007/01/21/aqua-spinners/"><span style="font-size: 10pt;">这里</span></a><span style="font-size: 10pt;">的图片那样<sup>[2]</sup>。<br />
&nbsp;&nbsp;&nbsp; 关于Applet加载时画面的更多内容请见发布文档的</span><a href="http://java.sun.com/javase/6/docs/technotes/guides/plugin/developer_guide/special_attributes.html"><span style="font-size: 10pt;">特别属性一节</span></a><span style="font-size: 10pt;">，以及</span><a href="https://jdk6.dev.java.net/plugin2/"><span style="font-size: 10pt;">关于这个新插件的说明</span></a><span style="font-size: 10pt;">。<br />
&nbsp;&nbsp;&nbsp; 本周就是这些了。在</span><a href="http://projects.joshy.org/demos/AnimatedStartup/applet.html"><span style="font-size: 10pt;">此处</span></a><span style="font-size: 10pt;">你可看到这个Demo的可运行版本。下次我将向你展示，在运行一个Applet之前，如何使用一个屏幕截图去替代这个真实的Applet。<br />
<br />
<strong style="font-size: 12pt;">译注<br />
</strong>[1]原文并未显示这张图片。<br />
[2]本句很有些奇怪，此处的两个"这里"应该指同一个地址。<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/236954.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-10-29 07:32 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/29/236954.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>出错时的软件开发(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/27/236562.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 27 Oct 2008 00:44:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/27/236562.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/236562.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/27/236562.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/236562.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/236562.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong style="font-size: 14pt;">出错时的软件开发</strong></span><br />
</div>
<span style="font-size: 10pt;">
在开发的过程中有错误发生了，此时你该如何应对呢？<a href="http://weblogs.java.net/blog/johnsmart/">John Ferguson Smart</a>在他的<a href="http://weblogs.java.net/blog/johnsmart/archive/2008/10/software_develo.html">最新博客</a>中提出了一些想法，大家可以参考一下(2008.10.27最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 现今比以后任何时候，都需要开发者更加高效。极度高效。组织需要提高从它们的开发项目中得到的附加值，并且它们也乐于寻找实现这一目标的方法。<br />
当然，你可以采用传统的方法--努力工作。为了消除项目中不可预见的症状，每天工作16个小时，还没有周末。但做的更聪明一点儿，会不会更好些？<br />
在引进新的实践方法及改进现有方法方面投入的相对多一些，以使组织能努力获得更多回报，这就是开发的过程。一般而言，事物中有许多方面都可以被改进，但此处有一些小窍门能使你的开发流程更加合理，只是为你开个头罢了。<br />
<br />
<strong style="font-size: 12pt;">持续集成(CI)通知策略的再思考<br />
</strong>&nbsp;&nbsp;&nbsp; 到目前为止，最通用的CI通知机制就是陈旧的邮件服务器。然而，你能肯定在你手边能完成这项任务的最合适系统就是电子邮件吗？尝试不使用电子邮件，而使用即时消息去完成你的CI通知。记住，电子邮件易成为一种干扰--如果你仅仅大约每两小时才查阅一次邮件，你就会变得十分高效。电子邮件只是，或至少是，用于构建失败--人们需要<em>快速</em>地知晓失败任务。<br />
<br />
<strong style="font-size: 12pt;">积极地优化构建过程</strong><br />
&nbsp;&nbsp;&nbsp; 构建度量(Build Metrics)是一种监控构建过程健康状况的极好方法。为什么过去3周中，代码覆盖率一直在下降？为什么单元测试的数量并未呈有规律的增长？为什么要花费很长的时间去修复这样的构建？运行单元测试需要多长时间--是否有一些测试需要执行过分长的时间？这些信息并非华而不实的东西--在不断改进构建过程的工作中，它们都扮演着关键的角色。现代CI工具，如Hudson，Bamboo和TeamCity，能为构建展示丰富的统计。Bamboo在这方面做的尤佳。无论你正使用何种CI工具，都要学习如何最大限度地使用它的报表特性，并使用这些特性去定位及修复开发过程中讨厌污点。如果你的CI工具不能给你所需要的全部信息，那就找一个能做到的。<br />
<br />
<strong style="font-size: 12pt;">合理化发布过程</strong><br />
&nbsp;&nbsp;&nbsp; 在发布过程中有许多必做的工作，如准备发行说明，确定该版本中哪些问题已被解决了，标记版本，等等。这些都是软件生命周期的重要部分，如果你忽略了它们，QA们和最终用户可能会很生气。但要尽量自动地去做这些工作。许多CI工具能很好地与问题追踪系统(如JIRA和Trac)进行集成，以便你能基于版本控制日志看到某个问题是在哪次特定的构建中被解决的。如果你在使用Eclipse，Mylyn能帮你将处理过的问题归总成逻辑变化组，并使用标准的模板列出在某项工作中已被解决的(或仅是影响到的)问题。或者你可使用Subversion的hook脚本去确保每次向Subversion做的提交都能对应到一个有效的问题编号。<br />
&nbsp;&nbsp;&nbsp; 这只是一些想法罢了--还有更多。底线就是--你不需要忍耐一个次理想的开发过程--相反，要进入其中，并做些能改进它的事情。祝好运! <br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/236562.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-10-27 08:44 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/27/236562.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NetBeans vs. Eclipse RCP: 插件机制比较(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/15/234341.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 15 Oct 2008 06:40:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/15/234341.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/234341.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/15/234341.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/234341.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/234341.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><span style="font-size: 14pt;"><strong>NetBeans vs. Eclipse RCP: 插件机制比较</strong></span></span><br />
<span style="font-size: 10pt;"><strong></strong></span></div>
<span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文是<a href="http://java.dzone.com/">JavaLobby</a>上的<a href="http://java.dzone.com/articles/netbeans-vs-eclipse-rcp-plugin">一篇小文</a>，作者对NetBeans和Eclipse的插件机制进行了一些比较，如有兴趣，可以看看。(2008.10.15最后更新)<br />
<br />
NetBeans与Eclipse处理扩展点及扩展的方式是不同的。让我们看看这些不同之处。请注意，本文仅针对那些对这两个平台的插件机制都具备相当知识的人。</span><br />
<br />
<strong style="font-size: 10pt;">NetBeans Platform:</strong><br />
</span>
<table bgcolor="#000000" border="0" cellpadding="1" cellspacing="1" width="686" height="56">
    <tbody>
        <tr style="background-color: #ffffff;">
            <td style="width: 100px; height: 15px;"><span style="font-size: 10pt;">定义扩展点</span></td>
            <td><span style="font-size: 10pt;">创建一个接口，并将它置入一个公共模块包中。</span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><span style="font-size: 10pt;">创建扩展</span></td>
            <td style="width: 573px; height: 21px;"  &="" gt;=""><span style="font-size: 10pt;">创建上述接口的实现，并通过layer.xml文件将它注册到虚拟文件系统中。</span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><span style="font-size: 10pt;">读取可用的扩展 </span></td>
            <td><span><span style="font-size: 10pt;">使用<a href="http://bits.netbeans.org/dev/javadoc/org- openide-util/org/openide/util /Lookup.html">org.openide.util.Lookup</a>类去获取上述接口实现的实例。</span><br />
            </span></td>
        </tr>
    </tbody>
</table>
<br />
<span style="font-size: 10pt;"><strong style="font-size: 12pt;"><span style="font-size: 10pt;"><strong>Eclipse RCP:</strong></span><br />
</strong></span>
<table bgcolor="#000000" border="0" cellpadding="2" cellspacing="1" width="876" height="129">
    <tbody>
        <tr style="background-color: #ffffff;">
            <td style="width: 100px; height: 15px;"><span style="font-size: 10pt;">定义扩展点</span></td>
            <td><span style="font-size: 10pt;">创建一个扩展点的描述符方案，该方案定义了扩展点的元素与属性，以及这些元素之间的关系。<br />
            可用的属性类型为：boolean，string，java，resource和identifer。可向该方案的任何部分添加文档。<br />
            最后，向plugin.xml文件 注册你的扩展点。</span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><span style="font-size: 10pt;">创建扩展</span></td>
            <td><span><span style="font-size: 10pt;">根据上述方案，在plugin.xml文件中创建一个子部分，在此处向属性赋予值。如果属性类型为java，则也要创建被引用的Java类。</span></span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><span style="font-size: 10pt;">读取可用的扩展</span></td>
            <td><span><span style="font-size: 10pt;">使用<a href="http://help.eclipse.org/ganymede/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/runtime_registry.htm">org.eclipse.core.runtime.IExtensionPoint</a>获取扩展的列表。从每个<em><a href="http://help.eclipse.org/ganymede/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IExtension.html">扩展</a></em>中获取<a href="http: //help.eclipse.org/ganymede/index.jsp?topic= /org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime /IConfigurationElement.html">IConfigurationElement</a>的列表：<br />
            每个IConfigurationElement对应于plugin.xml文件中的一个XML标签。</span></span></td>
        </tr>
    </tbody>
</table>
<br />
<table style="width: 1019px; height: 186px;" bgcolor="#000000" border="0" cellpadding="2" cellspacing="1" width="1019" height="186">
    <tbody>
        <tr style="background-color: #ffffff;">
            <td colspan="2" style="width: 100px; height: 15px;"><span style="font-size: 10pt;"><strong>NetBeans平台</strong></span></td>
            <td colspan="2"><span style="font-size: 10pt;"><strong>Eclipse RCP</strong></span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><span style="font-size: 10pt;">优点</span></td>
            <td><span style="font-size: 10pt;">缺点</span></td>
            <td><span style="font-size: 10pt;">优点</span></td>
            <td><span style="font-size: 10pt;">缺点</span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><span style="font-size: 10pt;">非常简单，易于学习</span></td>
            <td><span style="font-size: 10pt;">扩展点没有定义它自己：没有关于是哪个公共模块接口作为扩展点使用的信息。</span></td>
            <td><span><span style="font-size: 10pt;">扩展点清晰地定义了它自己：快速查看jar文件，你就能立即知道该插件提供了哪些扩展点，以及它们是由哪些元素组成的。</span></span></td>
            <td><span style="font-size: 10pt;">比较复杂，需要更多时间去学习</span></td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><br />
            </td>
            <td><span style="font-size: 10pt;">使用的查找名也未作定义</span></td>
            <td><span style="font-size: 10pt;">有许多属性类型：可以在XML文件中描述特定的结果。</span></td>
            <td><br />
            </td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><br />
            </td>
            <td><span style="font-size: 10pt;">扩展和其它的内容都混写在layer.xml文件中：难以确定该模块用于哪些扩展。</span></td>
            <td><span style="font-size: 10pt;">可生成扩展的多数组成部分，见PDE。</span></td>
            <td><br />
            </td>
        </tr>
        <tr style="background-color: #ffffff;">
            <td><br />
            </td>
            <td><br />
            </td>
            <td><span style="font-size: 10pt;">定义好了文档的位置。</span></td>
            <td><br />
            </td>
        </tr>
    </tbody>
</table>
<br />
<img src ="http://www.blogjava.net/jiangshachina/aggbug/234341.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-10-15 14:40 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/15/234341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>支持Unicode并不意味着应用是国际化的(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/14/234177.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 14 Oct 2008 02:51:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/14/234177.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/234177.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/14/234177.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/234177.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/234177.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">支持Unicode并不意味着应用是国际化的</span></span></strong><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <a href="http://joconner.com">John O'Conner</a>在他的<a href="http://joconner.com/unicode/unicode-support">最新Blog</a>中介绍了在使用Unicode<sup>[1]</sup>进行软件国际化的过程中普遍存在的一种误解，希望大家对有些启示。(2008.10.14最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 多年以来，我已经帮助了许多组织去国际化它们的软件产品。最普遍的误解之一就是Unicode会如何帮助它们的产品。有时候，客户会错误地相信，支持Unicode对于国际化他们的产品是足够的了。有时，他们相信"支持"Unicode只是一个单纯的是或不是，行或不行的能力问题，而不认为是在不同的场合和层次中去实现对Unicode的支持。<br />
&nbsp;&nbsp;&nbsp; Unicode是一个字符编码标准。它是一个很大的标准，有着许多的细微差别。你的产品可使用许多不同的方法去实现"Unicode支持"。其结果就是，那些产品将能以不同，但通常不会是所有的方式去操纵，处理，存储，甚至可能是展示世界上的文本。你的产品支持Unicode的能力并不是二元(是或不是)的；相反，你要理解到，产品是在各种不同层次上实现"Unicode支持"的。在多数简单的情况下，你的产品只需正确地存储并获取Unicode字符。在一种更复杂的层次下，你的产品可能要能够存储，查询或展示Unicode字符。再说一次，产品"支持"Unicode的能力并不能由一个单纯的是或不是的答案去评判。一般来说，产品会以某些方式，而不是别的什么方式，去支持Unicode。<br />
&nbsp;&nbsp;&nbsp; 哪怕是在最复杂的层次中实现了Unicode支持，也不意味着你的产品是<strong>国际化的</strong>。国际化是使一个软件代码库易于本地化的过程。国际化过程会创建一个产品，该产品没有针对某个单一文化或语言的特殊偏见。该产品可对特定文化进行本地化。支持Unicode是国际化工作的一个关键组成部分，但它也仅仅只是一个组成部分罢了。与支持Unicode一样，你的国际化支持也有着在不同层次上的复杂度与能力。<br />
&nbsp;&nbsp;&nbsp; 概述之，产品可用不同的方式去支持Unicode。支持Unicode通常并不表示你的产品有能力去完成各种可能的针对Unicode的功能。相反，"支持"常意味着你可以使用Unicode，而不是别的什么东西，去做某些事情。另外，对Unicode的支持并不是国际化你的产品的唯一步骤。 Unicode只是其中的一个步骤，重要的一步罢了。国际化是创建一个易于进行本地化的产品的过程，这样的产品消除了文化偏见，以便在对其进行了本地化之后仍能够很容易地去支持另一种文化或地域。你可将Unicode作为国际化工作的一个步骤，但Unicode本身并不能创建一个国际化产品。<br />
&nbsp;&nbsp;&nbsp; 如果你有问题关于Unicode如何才能帮助你的产品，请联系我或留下评论。如果我能帮忙，就会回复。如果<em>我</em>不能，我也知道谁能。 <br />
<br />
<strong>
译注</strong><br />
[1]Unicode是一个字符集标准。该标准为全球书面和口头语言，计算机平台，或应用程序所使用的每个字符分配了一个独一无二的数值。Unicode包含了其它有限字符集所使用的全部字符。在Unicode之前，小字符集彼此之间分配的字符数值是不同的。Unicode统一了所有的其它字符集；每个字符拥有它自己的，唯一的数值。<br />
P.S. 这段Unicode的简介也出自于Conner的一篇Blog--<a href="http://joconner.com/unicode/what-is-unicode">What is Unicode</a>。
</span><br />
<span style="font-size: 10pt;"><br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/234177.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-10-14 10:51 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/14/234177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写好的面向对象代码(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 07 Oct 2008 09:06:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/232942.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/232942.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/232942.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong style="font-size: 14pt;">编写好的面向对象代码</strong></span><br />
</div>
<p align="left"><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文是<a href="http://www.java.net">java.net</a>上的一篇<a href="http://weblogs.java.net/blog/thedarksavant/archive/2008/10/writing_great_o_1.html">博客</a>，作者<a href="http://weblogs.java.net/blog/thedarksavant/">Curtis Cooley</a>对编写好的面向对象代码有些建议，希望对大家都有所帮助。(2008.10.08最后更新)<br />
</span><span style="font-size: 10pt;"><br />
获取经验没有捷径。编写好的面向对象代码需要经验，但这儿有三种做法能帮你在一开始就很顺利，即便你是老顽固：<br />
&nbsp;&nbsp;&nbsp; 1. 使用测试驱动开发(TDD)编写你所有的代码<br />
&nbsp;&nbsp;&nbsp; 2. 遵循<a href="http://xp.c2.com/XpSimplicityRules.html">简单法则</a><br />
&nbsp;&nbsp;&nbsp; 3. 告之而非问之<br />
<br />
</span><span style="font-size: 10pt;"><strong style="font-size: 12pt;">使用TDD编写所有代码</strong><br />
&nbsp;&nbsp;&nbsp; 按<a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/tdd-is-design-activity.html">测试先行</a>编写的代码与按测试后行编写的代码是极为不同的代码。按测试先行编写的代码是松耦合与高聚合的。当某个属性或私有方法需要暴露给测试程序时，按测试后行编写的代码常会打破封装，因为该类并不是为了测试而设计的。如果你首先编写测试代码，你的依赖将会更好，你的代码将是松耦合与高聚合的。后面会有更多关于测试能帮助你设计更佳代码的内容。<br />
<br />
<strong style="font-size: 12pt;">遵循简单法则</strong><br />
&nbsp;&nbsp;&nbsp; 代码是简单的，只要当它：<br />
&nbsp;&nbsp;&nbsp; 1. 执行了所有的测试<br />
&nbsp;&nbsp;&nbsp; 2. 不包含重复<br />
&nbsp;&nbsp;&nbsp; 3. 表达了所有的意图<br />
&nbsp;&nbsp;&nbsp; 4. 使用最少的类和方法<br />
注意到我用的是个被排序了的列表是很重要的。顺序是重要的。只有一个main()方法的的GodClass<sup>[1]</sup>不会是简单的。这个类可能执行了所有的测试，但在任何比"Hello, world!"更复杂的程序中，它肯定包含了重复，并且也没有表达出全部的意图。<br />
我努力使用简单法则去关注<a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/if-bugs.html">If问题</a>。我不知道如何使用简单法则去阻止某人编写重量级的If代码。有人可能会提出不同意见，我也尝试过，但这样的重量级If代码确实无法表达意图。但当你阅读如下代码时</span></p>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.STANDARD)&nbsp;{<br />
&nbsp;&nbsp;alert();<br />
}</span></div>
<span style="font-size: 10pt;">确实难以看出其中的意图。这些代码无论处于哪个方法的上下文环境中，我们都能知道，如果mobile是STANDARD类型的话，那么就报警。而你所需要的更多意图呢？<br />
我还有一点儿灵感显现。如果有那样的代码，那么在其它地方肯定还会有更多那样的代码。这些代码可能就像：<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.GAS)&nbsp;{<br />
&nbsp;&nbsp;registerGasReading();<br />
}</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.TEXT)&nbsp;{<br />
&nbsp;&nbsp;sendTextMessage();<br />
}</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.LOCATION)&nbsp;{<br />
&nbsp;&nbsp;notifyLocation();<br />
}</span></div>
<span style="font-size: 10pt;">你看出来了吗？我是看出来了。它违反了规则2，有很多地方都违反了规则2，并且是一种最坏的情形。这段代码有多处重复。重复将极难发现。所以，请帮助防止这种情形的发生，我已包含其中了。<br />
<br />
<strong style="font-size: 12pt;">告之而非问之</strong><br />
简言之，<a href="http://www.pragmaticprogrammer.com/articles/tell-dont-ask">告之而非问之</a>意指不要先问一个对象的状态，然后才让它去工作。而应该告之对象如何去工作。这就意味着之前所有的那些If例子应该变为：</span> <br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.alert();</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.registerGasReading();</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.sendTextMessage();</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.notifyLocation();</span></div>
<span style="font-size: 10pt;">现假设遍布该程序中的一些If语句块有重复的实现。在"重量级If"版本的程序中，可能很难发现它们；但在"告之而非问之"版本的程序中，所有的实现都在Mobile中。所有的实现都在一处，这就便于察觉并根除问题。<br />
&nbsp;&nbsp;&nbsp; 倾听你的测试程序也能帮助你保持代码的简洁。</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<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);">interface</span><span style="color: rgb(0, 0, 0);">&nbsp;Alarm&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;alert(Mobile&nbsp;mobile);<br />
}<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;Siren&nbsp;</span><span style="color: rgb(0, 0, 255);">implements</span><span style="color: rgb(0, 0, 0);">&nbsp;Alarm&nbsp;{<br />
&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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;alert(Mobile&nbsp;mobile)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.STANDARD)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;soundSiren();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
}<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;TestSiren&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;TestCase&nbsp;{<br />
&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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;test_alert()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;LocationMobile&nbsp;mobile&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;LocationMobile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Siren&nbsp;siren&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;Siren();<br />
&nbsp;&nbsp;&nbsp;&nbsp;siren.alert(mobile);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">assert</span><span style="color: rgb(0, 0, 0);">(sirenSounded());<br />
&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">如果你密切地倾听测试程序，它可能会问你，"为什么你需要一个LocationMobile去测试Siren呢？"的确，为什么呢？看起来，Siren应该还不知道LocationMobile吧。</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<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);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;LocationMobile&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);">&nbsp;Alarm&nbsp;alarm;<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;LocationMobile(Alarm&nbsp;alarm)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.alarm&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;alarm;<br />
&nbsp;&nbsp;}<br />
&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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;alert()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;alarm.alert();&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;alert&nbsp;on&nbsp;Alarm&nbsp;no&nbsp;longer&nbsp;needs&nbsp;a&nbsp;mobile</span><span style="color: rgb(0, 128, 0);"><br />
</span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;}<br />
}<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;TestLocationMobile()&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;TestCase&nbsp;{<br />
&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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;test_alert()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Alarm&nbsp;alarm&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;EasyMock.createMock(Alarm.</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;alarm.alert();<br />
&nbsp;&nbsp;&nbsp;&nbsp;EasyMock.replay(alarm);<br />
&nbsp;&nbsp;&nbsp;&nbsp;Mobile&nbsp;mobile&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;LocationMobile(alarm);<br />
&nbsp;&nbsp;&nbsp;&nbsp;mobile.alert();<br />
&nbsp;&nbsp;&nbsp;&nbsp;EasyMock.verify(alarm);<br />
}</span></div>
<span style="font-size: 10pt;">好像我只是交换了依赖关系。Alarm不再依赖Mobile，现在是Mobile依赖Alarm。但如果你仔细地观察这个测试程序，你会发现真正的依赖关系是，Siren知晓了LocationMobile。一个具体类依赖另一个具体类，这违反了<a href="http://c2.com/cgi/wiki?DependencyInversionPrinciple">依赖反转原则</a>(DIP)。第二个例子就让LocationMobile依赖Alarm接口。具体类依赖抽象，这就满足DIP了。<br />
&nbsp;&nbsp;&nbsp; 如果你使用TDD，并遵循简单法则和告之而非问之原则去编写所有的代码，你就处于成为一个更好的面向对象程序员的道路上了。好的面向对象代码易于阅读和维护，但难以编写，至少，在开始时是这样的。你写的越多，你就会变得越好，也会获得更多的经验。同时，这些实践经验也会使你在自己的道路上受益匪浅。<br />
<br />
<strong>译注</strong><br />
[1]GodClass(上帝类)指包含了太多内容的类。<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/232942.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-10-07 17:06 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>下一代Java Applet插件技术(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/07/14/214703.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 14 Jul 2008 11:56:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/07/14/214703.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/214703.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/07/14/214703.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/214703.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/214703.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 14pt;">下一代Java Applet插件技术</span></strong><br />
</div>
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">Java SE 6对Java桌面应用进行较大的升级，并启动了Java SE 6 Update N计划，该计划旨在简化JRE的大小，增进用户的安装体验，并提供了一个新的Applet浏览器插件，该插件将会随<a href="https://jdk6.dev.java.net/6u10ea.html">Java SE 6 Update 10</a>发布。<a href="http://java.sun.com/developer/technicalArticles/javase/newapplets/">本文</a>全面介绍了这个新插件的关键特性，并以<a href="http://www.nasa.gov/">NASA</a>的<a href="http://worldwind.arc.nasa.gov/java/">World Wind</a>为例介绍了该插件的应用。(2008.07.15最后更新)<br />
<br />
</span><span style="font-size: 10pt;">Applet回来了！<br />
为了在网络中传递你的程序，是时候再次考虑Java Applet技术了。<a href="http://jdk6.dev.java.net/6u10ea.html">下一代Java插件技术</a>以一种不同的，比过去更高效、更可靠的途径来运行Applet。现在你可以获得如下好处：<br />
</span>
<ul>
    <li><span style="font-size: 10pt;">
    增强的可靠性</span></li>
    <li><span style="font-size: 10pt;">
    改进的用户体验</span></li>
    <li><span style="font-size: 10pt;">
    在后台启动Applet</span></li>
    <li><span style="font-size: 10pt;">
    内建的JNLP支持</span></li>
    <li><span style="font-size: 10pt;">
    针对每个Applet的命令行参数</span></li>
    <li><span style="font-size: 10pt;">
    堆内存大小，Java 2D API加速选项</span></li>
    <li><span style="font-size: 10pt;">
    改进的Java/JavaScript程序设计语言集成</span></li>
    <li><span style="font-size: 10pt;">
    改进的Windows Vista支持</span></li>
    <li><span style="font-size: 10pt;">
    签名的Applet现在可以在Internet Explorer的保护模式中正常运行</span></li>
</ul>
<span style="font-size: 10pt;">
下一代Java插件提供了一种完全重新设计的架构，它将出现在<a href="http://jdk6.dev.java.net/6u10ea.html">Java SE 6 Update 10</a>中。该插件为运行在网络浏览器中的Applet提供了强大的新功能，它以向后兼容的方式改进了整个Applet的可靠性及功能。<br />
下一代Java插件最有意义的新特性是它内建支持通过JNLP文件启动Applet。使用JNLP文件格式作为Applet的描述符就能允许Applet马上复用之前为Java Web Start应用所写的JNLP扩展。<br />
<strong><br />
<span style="font-size: 12pt;">执行Applet的新途径</span></strong><br />
执行Applet的新途径在结构上与Java Web Start技术相似，但与浏览器整合的更为紧密。Applet不在运行于网络浏览器内的JVM中，而是会启动一个独立的JVM进程去运行Applet。默认地，只有一个JVM将被启动，但你也能启动多个JVM，并且可以为每个Applet都设置命令行参数，所以你能影响堆内存的大小或其它的要求。<br />
<img alt="" src="http://java.sun.com/developer/technicalArticles/javase/newapplets/images/architecture.jpg" width="424" height="320" /><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span><span style="font-size: 10pt;"><strong>Figure 1.</strong> <em>Applet Architecture</em></span><br />
<span style="font-size: 10pt;">
在上图中，云表示JVM实例。在浏览器内有一个小的，headless JVM被用于管理一个或多个客户端JVM之间的连接，这些JVM运行着Applet。在该图中，Duke表示Applet。其中，一个JVM实例运行着两个Applet，另一个运行着一个Applet。<br />
Applet直接从JNLP文件启动，它使用的JNLP文件与Java Web Start软件使用的描述符文件相同，并且允许使用比典型的"archive"，"code"和"cache_archive"更为强大的参数。<br />
新的插件提供了：<br />
</span>
<ul>
    <li><span style="font-size: 10pt;">
    能够访问之前仅由Java Web Start软件专用的高级JNLP扩展。之前有少部分参数能够使用，但有一些限制，现在这些限制则被去除。</span></li>
    <li><span style="font-size: 10pt;">
    通过Applet访问JNLP API。</span></li>
    <li><span style="font-size: 10pt;">
    支持PersistenceService和DownloadService。</span></li>
    <li><span style="font-size: 10pt;">
    能够控制堆内存大小，命令行参数，JRE版本选择和自动下载。你具有Java Web Start软件所拥有的相同功能。</span></li>
</ul>
<span style="font-size: 10pt;">
现在你就可以在Web页面中使用像下面这样的语句了：<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;">&lt;</span><span style="color: #000000;">applet&nbsp;width</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;</span><span style="color: #000000;">500</span><span style="color: #000000;">&#8221;&nbsp;height</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;</span><span style="color: #000000;">500</span><span style="color: #000000;">&#8221;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">param&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;jnlp_href&#8221;&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;my_applet.jnlp&#8221;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">applet</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">调用Applet生命周期方法init，start，stop和destroy会更为确定，并且已经改进了跨浏览器行为。完全支持Applet类装载器缓存，遗留的Applet生命周期及对向后兼容性的需求，并且这些行为都已得到了改进。<br />
Applet运行的就像一个由Java Web Start启动的应用。参数jnlp_href在Web页面和Applet的JNLP描述之间起到了桥接的作用。在如宽度与高度这样的特定方面，Applet标签与JNLP文件具有重叠的机制。<br />
一般地，你应该使用<a href="https://jdk6.dev.java.net/testDT.html">Deployment Toolkit</a>，这也是一个出现在Java SE 6 Update 10中的新工具，它能自动地为Applet标签生成HTML。<a href="https://jdk6.dev.java.net/deployment_advice.html">部署建议</a>指南展示了如何使用Deployment Toolkit简便地发布Applet。<br />
<br />
<strong><span style="font-size: 12pt;">配置Applet</span></strong><br />
现在也能更为简单地在多个方面来配置Applet，包括堆内存大小，需要被使用的Java版本，类加载器缓存，边界，及其它。<br />
&lt;applet&gt;与JNLP文件在针对某些参数时有重叠的机制。这些冲突可以用如下方法解决：<br />
</span>
<ul>
    <li><span style="font-size: 10pt;"><strong>width and height</strong>：这些属性将总是从&lt;applet&gt;，而不是JNLP文件，中获取。这是假设浏览器知道Applet在Web页面上应该显示多大，也只有浏览器才能支持相对于页面的宽度与高度(例如，width="50%")。</span></li>
    <li><span style="font-size: 10pt;">
    <strong>codebase</strong>：如果JNLP文件在&lt;jnlp&gt;标签中指定了一个绝对的codebase，那么就使用它。否则，将使用在<a href="https://jdk6.dev.java.net/plugin2/jnlp/#CODEBASE">codebase handling</a>一节中描述的规则进行组织。</span></li>
    <li><span style="font-size: 10pt;">
    <strong>code</strong>：当指定了jnlp_href参数，Applet的主类名将从main-class参数换成JNLP文件中的applet-desc标签，并且code属性会被忽略。注意，该特性允许你为经典Java插件写一个拥有反馈的Applet标签，但在新的Java插件中，该标签可使用更高级的功能。请见下面的"兼容性"一节。</span></li>
    <li><span style="font-size: 10pt;">
    任何一个由&lt;param&gt;标签指定的Applet参数将与JNLP文件中指定的参数进行合并。如果&lt;applet&gt;标签和JNLP文件都指定了同一个参数，&lt;applet&gt;标签中的版本将覆盖JNLP文件中的版本，除了参数java_arguments和java_version。</span></li>
    <li><span style="font-size: 10pt;">
    新的java_arguments和java_version参数在JNLP Applet中是不必要的。会替换为通过JNLP文件请求JRE版本或向JVM传递参数的机制。所以，命令行参数和JNLP文件请求的JRE版本将会覆盖HTML中为Applet指定的这些值。</span></li>
    <li><span style="font-size: 10pt;">
    特定的参数，例如image，boxbgcolor等等，在Applet的启动过程中是有用的。在HTML而不是JNLP文件中指定这些参数可能更好些，以便于在加载Web页面时就可立即获取它们，而不用再等到单独下载JNLP文件之后。</span></li>
</ul>
<span style="font-size: 10pt;">
过去，通过Java控制面板设置最大堆内存是有限制的。在新的Java插件中，这些限制被取消。现在Applet可以像命令行应用那样使用大量堆空间。<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: #000000;">&lt;</span><span style="color: #000000;">APPLET&nbsp;archive</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">my_applet.jar</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;code</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">MyApplet</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;width</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">300</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;height</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">300</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PARAM&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">java_arguments</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">-Xmx128m</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">APPLET</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">指定一个非默认大小的堆内存以及一个Java 2D硬件加速器选项，该选项常通过JOGL使用OpenGL应用于Applet。<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;">&lt;</span><span style="color: #000000;">APPLET&nbsp;archive</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">my_applet.jar</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;code</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">MyApplet</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;width</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">300</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;height</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">300</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PARAM&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">java_arguments</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">-Xmx256m&nbsp;-Dsun.java2d.noddraw=true</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">APPLET</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">如果你喜欢，一个Applet可强制进入一个属于它自己的JVM实例，而与所有其它的Applet隔离开：<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;">&lt;</span><span style="color: #000000;">param&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;separate_jvm&#8221;&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">&#8221;&nbsp;</span><span style="color: #000000;">/&gt;</span></div>
<span style="font-size: 10pt;">当把某些桌面应用移植到Web浏览器时，这就有用了。<br />
你也能使特定的Applet运行在特定版本的JRE上，如下所示：<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;">&lt;</span><span style="color: #000000;">j2se&nbsp;version</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;</span><span style="color: #000000;">1.4</span><span style="color: #000000;">+</span><span style="color: #000000;">&#8221;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">j2se&nbsp;version</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;</span><span style="color: #000000;">1.5</span><span style="color: #000000;">*</span><span style="color: #000000;">&#8221;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">当想针对特定版本的JRE，或Applet取代早期版本的选择机制(如同IE浏览器中的CLSID)，进行质量测评时，该方法就很有用了。如果请求了一个非常老的JRE版本，就会强制执行限制；如果Applet试图加载未签名的代码，将会提示用户。<br />
注意，因为支持JNLP的Java插件是在Java SE 6 Update 10中才首次出现的，所以指定像&#8220;1.4+&#8221;这样的版本基本上没有意义的。当需要&#8220;1.7+&#8221;时，这才有意义。<br />
另外，你可以在JNLP文件中使用&lt;update&gt;标签来显著降低第二次及接下来各次启动的时间。<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;">&lt;</span><span style="color: #000000;">update&nbsp;check</span><span style="color: #000000;">=</span><span style="color: #000000;">&#8221;background&#8221;</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">在这种情况下，将使用缓存中已有的Applet程序，并且在后台下载该应用的更新版本。在下次启动时，就会使用新版本。<br />
新的插件也能更好地对图像进行定制，在Applet被加载之前会展示该图像。image参数会以支持动画GIF文件作为目标，<a href="http://java.sun.com/javase/6/docs/technotes/guides/plugin/developer_guide/contents.html">Java Plug-in Developers' Guide</a>的<a href="http://java.sun.com/javase/6/docs/technotes/guides/plugin/developer_guide/special_attributes.html">Special Attributes</a>一节对此有描述。此外，现也支持如下新的参数：<br />
<strong>boxborder</strong><br />
一个布尔型参数，用于指定在Applet被加载之前是否在Applet区域的边缘绘制一个宽度一象素的边框。默认为true。我们建议将该值设置为false，特别是将一个动画GIF用作加载期图像时，以避免可能的闪烁。<br />
<strong>centerimage</strong><br />
一个布尔型参数，用于指定是否将加载期图像在Applet区域内居中显示，而不是从左上角起始。默认为false。<br />
使用参数boxborder和centerimage的例子：<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;">&lt;</span><span style="color: #000000;">APPLET&nbsp;archive</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">large_archive.jar</span><span style="color: #000000;">"</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;code</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">MyApplet</span><span style="color: #000000;">"</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;width</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">300</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;height</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">300</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;!--</span><span style="color: #000000;">&nbsp;Use&nbsp;an&nbsp;animated&nbsp;GIF&nbsp;as&nbsp;an&nbsp;indeterminate&nbsp;progress&nbsp;bar<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;the&nbsp;applet&nbsp;is&nbsp;loading&nbsp;</span><span style="color: #000000;">--&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PARAM&nbsp;NAME</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">image</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;VALUE</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">animated_gif.gif</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;!--</span><span style="color: #000000;">&nbsp;Turn&nbsp;off&nbsp;the&nbsp;box&nbsp;border&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;better&nbsp;blending&nbsp;</span><span style="color: #0000ff;">with</span><span style="color: #000000;">&nbsp;the<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;surrounding&nbsp;web&nbsp;page&nbsp;</span><span style="color: #000000;">--&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PARAM&nbsp;NAME</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">boxborder</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;VALUE</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">false</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;!--</span><span style="color: #000000;">&nbsp;Center&nbsp;the&nbsp;image&nbsp;</span><span style="color: #0000ff;">in</span><span style="color: #000000;">&nbsp;the&nbsp;applet's&nbsp;area&nbsp;</span><span style="color: #000000;">--&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PARAM&nbsp;NAME</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">centerimage</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;VALUE</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">true</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">APPLET</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;"><br />
<strong><span style="font-size: 12pt;">兼容性</span></strong><br />
现在可更容易维护向后兼容性。你可创建运行在更早Java插件版本上的程序，但仅需提供一个与jnlp_href参数一样的格式完整的&lt;applet&gt;标签就可使用这些新特性了。早期版本的JRE会忽略jnlp_href参数，转而使用&lt;applet&gt;标签。新的Java插件技术将忽略archive和code参数，而仅使用JNLP文件去启动Applet。<br />
<br />
<strong><span style="font-size: 12pt;">World Wind Applet示例</span></strong><br />
由World Wind Java开发组创建的<a href="http://download.java.net/javadesktop/plugin2/wwj/">NASA World Wind Java</a> Applet示例阐述了如果发布像NASA World Wind Java这样的领先类库。同样地，也用示例说明了如何使用JavaScript在Web页面中高效地整合HTML和Applet内容。<br />
<img alt="" src="http://java.sun.com/developer/technicalArticles/javase/newapplets/images/nasaapplet.jpg" width="450" height="283" /><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
</span><span style="font-size: 10pt;"><strong>Figure 2.</strong> </span><em><span style="font-size: 10pt;">NASA World Wind Applet</span></em><br />
<span style="font-size: 10pt;">该Web页面包含了关于喀斯喀特山脉的信息(要感谢维基百科)，并且将World Wind Java作为一个Applet嵌入其中，以图示该山脉中各山的位置。<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;">&lt;</span><span style="color: #000000;">applet&nbsp;id</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">wwjApplet</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;width</span><span style="color: #000000;">=</span><span style="color: #000000;">600</span><span style="color: #000000;">&nbsp;height</span><span style="color: #000000;">=</span><span style="color: #000000;">380</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;code</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">gov.nasa.worldwind.examples.applet.WWJApplet</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;archive</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">BackwardCompatibility.jar</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">param&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">jnlp_href</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">WWJApplet.jnlp</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">applet</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">WWJApplet随标准的<a href="http://worldwind.arc.nasa.gov/java/">World Wind Java发行包</a>发布。如下所述，你可选择编写你自己的Applet类并将World Wind嵌入其中：<br />
下面是WWJApplet.jnlp文件中相关的部分：<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;">&lt;</span><span style="color: #000000;">jnlp&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">WWJApplet.jnlp</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">resources&nbsp;os</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">Windows</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">property&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">sun.java2d.noddraw</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">true</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">resources</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">resources</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">j2se&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">http://java.sun.com/products/autodl/j2se</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;version</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">1.4+</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">jar&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">worldwind.jar</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;main</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">true</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">extension&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">jogl</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">resources</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">applet</span><span style="color: #000000;">-</span><span style="color: #000000;">descname</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">WWJ&nbsp;Applet</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;main</span><span style="color: #000000;">-</span><span style="color: #000000;">class</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">gov.nasa.worldwind.examples.applet.WWJApplet</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;!--</span><span style="color: #000000;">&nbsp;Overwritten&nbsp;by&nbsp;the&nbsp;surrounding&nbsp;web&nbsp;page&nbsp;</span><span style="color: #000000;">--&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;width</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">100</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;height</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">100</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">applet</span><span style="color: #000000;">-</span><span style="color: #000000;">desc</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">jnlp</span><span style="color: #000000;">&gt;</span></div>
<span style="font-size: 10pt;">注意几点：<br />
</span>
<ul>
    <li><span style="font-size: 10pt;">
    在本例中，worldwind.jar作为主类使用。理想地，从NASA的网站引用它，将其作为一个JNLP扩展，这就使得许多不同的都嵌入了World Wind的应用程序或Applet共享相同的jar文件。详情请见下面的内容。</span></li>
    <li><span style="font-size: 10pt;">
    为了它的硬件加速的3D图形，World Wind Java使用了针对OpenGL API的Java绑定，<a href="http://jogl.dev.java.net/">JOGL</a>。注意，JOGL JNLP扩展仅使用一行代码与应用程序进行结合。也要注意，在Windows平台上，由于OpenGL API与DirectDraw/Direct3D API(该API用于Windows平台默认的Java 2D实现)之间在驱动层面的冲突，需要指定系统参数-Dsun.java2d.noddraw=true。Windows平台上所有使用JOGL的应用程序与Applet程序都需要该系统参数。</span></li>
</ul>
<span style="font-size: 10pt;">
Web页面中的HTML链接调用JavaScript函数，该函数会与Applet进行交互并将其导向合适的山峰。下面是这些链接中的一个：<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;">&lt;</span><span style="color: #000000;">a&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">javascript:gotoLocation(MOUNT_RAINIER);</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">Mount&nbsp;Rainier</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">a</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;(southeast&nbsp;of&nbsp;Tacoma,&nbsp;Washington)&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span></div>
<span style="font-size: 10pt;">当点击该链接后，将会调用JavaScript函数gotoLocation。该函数定义在同一个Web页面中：<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;">function</span><span style="color: #000000;">&nbsp;gotoLocation(locationString)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">var</span><span style="color: #000000;">&nbsp;params&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;locationString.split(';');<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(params.length&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">)&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Lat/lon</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;getWWJApplet().gotoLatLon(parseFloat(params[</span><span style="color: #000000;">1</span><span style="color: #000000;">]),<br />
&nbsp;&nbsp;&nbsp;&nbsp;parseFloat(params[</span><span style="color: #000000;">2</span><span style="color: #000000;">]));<br />
&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />
}</span></div>
<span style="font-size: 10pt;">Web页面HTML中的山峰位置将被解码为JavaScript字符串。将从这些字符串中解析出纬度，经度及其它视觉信息，并将它们传递给Applet。 gotoLatLon方法是在WWJApplet类中定义的；上面的方法调用将起动一个JavaScript-to-Java调用，把参数从JavaScript引擎传给Java。World Wind Applet接收该通知，并将视点以动画的方式切换到适当的地方。注意，gotoLatLon方法会迅速地返回，以便浏览器不必等待它的完成；该动画会在一个单独的Java线程中运行。<br />
<img alt="" src="http://java.sun.com/developer/technicalArticles/javase/newapplets/images/mtsthelens.jpg" /><br />
&nbsp;&nbsp;
</span><span style="font-size: 10pt;"><strong>Figure 3.</strong> <em>World Wind Applet with Mount St. Helen's Clicked</em><br />
</span>
<span style="font-size: 10pt;">如上所述，将World Wind Java集成到你的应用程序或Applet程序中的最好方法是将其作为一个JNLP扩展。这允许很多来自网络的集成了World Wind Java的应用程序或Applet程序能够共享World Wind代码资源。为了引用World Wind JNLP扩展，你需将下面的语句行加入到你的应用程序或Applet程序的JNLP文件中的&lt;resources&gt;部分：<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;">&lt;</span><span style="color: #000000;">extension&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">worldwind</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">http://worldwind.arc.nasa.gov/java/0.4.1/webstart/worldwind.jnlp</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">extension&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">jogl</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;href</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;</span></div>
<span style="font-size: 10pt;">注意，World Wind扩展JNLP是区分版本的，所以你需参考World Wind文档或访问<a href="http://forum.worldwindcentral.com/forumdisplay.php?f=37">论坛</a>去找到你的JNLP会引用到的扩展的最新版本。<a href="http://worldwindcentral.com/">World Wind Central</a>是一个关于World Wind最新信息的有用资源。<br />
将World Wind作为一个扩展使用就意味着你不能将WWJApplet直接作为你的main-class使用。由于JNLP文件格式的语义，主jar (main="true")必须定义在主JNLP文件中。但很容易就能适应该限制，你可简单地创建你自己的WWJApplet子类(称之为MyWWJApplet)，而它并不做任何事情：<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;">class&nbsp;MyWWJApplet&nbsp;extends&nbsp;WWJApplet&nbsp;{}</span></div>
<span style="font-size: 10pt;">将worldwind.jar置于classpath中，并编译上述类，然后将该类放入它自己的jar文件中。引入这个jar作为你的主jar，MyWWJApplet就成为了你的main-class，然后将其作为JNLP扩展引入到World Wind中。<br />
<br />
<strong><span style="font-size: 12pt;">结论</span></strong><br />
介绍了Java插件对JNLP的支持，这为Applet的发布提供了很多新的可能，这对在浏览器内外发布Java内容的方法的统一又进了一大步。Applet自从它们起始已过了很长的时间，现在随着对JNLP的支持，它们会比以往更快，也更易于定制。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/214703.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-14 19:56 <a href="http://www.blogjava.net/jiangshachina/archive/2008/07/14/214703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>何时编写单元测试？(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 09 Jun 2008 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/206812.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/206812.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/206812.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;">&nbsp;&nbsp;&nbsp; 是在编写一个方法之前就编写它的单元测试，还是在写完这个方法，甚至是整个类之后才编写单元测试呢？John Ferguson Smart<sup>[1]</sup>在他的<a href="http://weblogs.java.net/blog/johnsmart/archive/2008/06/tests_first_or.html">blog</a>中再次提出了这个问题，并根据自己的经验给出了一些建议。(2008.06.10最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 都别书生气。在你编写一个方法之前或是之后编写单元测试--根据我的经验，只要你在编写代码的<em>几乎同时</em>就考虑并编写单元测试程序，那么这就无关紧要了。过后再返回去(或者根本就不回去)写测试程序将导致问题。就我个人而言，我喜欢在编写少量代码之前或紧接着的之后就编写单元测试--这不会打破工作流程，因为<em>它就是流程的一部分</em>。<br />
&nbsp;&nbsp;&nbsp; 这需要一点儿实践经验--缺乏经验的开发者经常为要写什么样的测试程序而烦恼。但这可能也反映出一个事实：他们同样也不知道要写什么样的代码。一些人评说TDD能够鼓励进行微设计--一种非常底层的设计，它不需要考虑较大的场景。这会发生在缺乏经验的开发者身上；如果你教条般地应用这种方法，同样也会遇上。像行为驱动开发这样的方法在此处就会很酷。当你在写getter方法之前，你会写一个针对这个getter方法的单元测试吗？如果是的话，那么你的单元测试专注的层次就较高了，也会更接近于用户(或系统)的需求。<br />
&nbsp;&nbsp;&nbsp; 回到问题的本质，为什么我喜欢把单元测试放在最开始的位置？很简单！我的实践经验告诉我，那样可以帮助提高代码的质量，并且节约调试时间。在开始时写十个小的单元测试所花的时间比在以后修复Bug所花的时间要少，如果代码经过了正确的单元测试，那就不会有Bug了。<br />
&nbsp;&nbsp;&nbsp; 事实上，我屡屡见到，如果某些代码经过了适当的单元测试，那么就不会有编码问题。最近就有一个例子：花了一个小时的时间去搜寻Web应用中的一个问题，该问题出现在一个编写正确的Spring-MVC程序中。结果是由于一个检验器类忽略了一个异常。很容易就发现了这个问题，实际上，在看了代码(代码检查(Code Review)也很有效)之后立刻就发现了。但关键是，我们花了一个小时甚至更多的时间去找这个需要进行检查的类。如果这些代码经过了适当的测试，那么就能很快地发现并解决这个问题。<br />
&nbsp;&nbsp;&nbsp; 根据我的经验，当人们在编写完程序之后才开始编写单元测试，就如同事后才有这样的想法，他们很难写出这些测试了 ("我已经完成了所有的代码，此时我还得去写单元测试")。或者根本就不去做。在这种情况下，代码是否完成了呢？如果代码运行地很好，那就算是完成了。这样的话，再写单元测试就大大地丧失了它的价值。还不仅如此，事后编写的单元测试将是肤浅的，不会对代码进行良好地测试。或者，开发者已经耗完了时间，他们根本就不想再为单元测试伤神了。<br />
&nbsp;&nbsp;&nbsp; TDD与任何其它的编码实践一样。当你正在学习某个新的技术时，你会倾向于对学习指导亦步亦趋。类似地，当你学习一项武术时，你也会试着一步步地模仿大师的动作，而不必去理解其中的逻辑。一旦你熟悉了某个技术，能够熟练地使用它，并对它有了更深入地理解，<em>然后</em>，你就能改进它，并与你之前掌握的其它技术进行溶合了。<br />
<br />
<strong><span style="font-size: 12pt;">
译注</span></strong><br />
[1]John是<a href="http://www.javapowertools.com/">Java Power Tools</a>一书的主作者，也是<a href="http://www.java.net/">java.net</a>中一位活跃的<a href="http://weblogs.java.net/blog/johnsmart/">Blogger</a>。<br />
<br />
<strong><span style="font-size: 12pt;">译后</span></strong><br />
&nbsp;&nbsp;&nbsp; 上周在java.net上看到这篇Blog，再联想到自己在平时工作中的单元测试实践，有些感触，故将其翻译了出来，与大家共享。<br />
&nbsp;&nbsp;&nbsp; 事先就编写单元测试，还是事后才编写单元测试？这是一个老问题。按照TDD的思想，自然是要先编写单元测试，然后再编写能够通过该单元测试的方法。<br />
&nbsp;&nbsp;&nbsp; 但，单元测试并不是TDD的专属领地，很多不实践TDD的项目也在应用着单元测试。<br />
&nbsp;&nbsp;&nbsp; 我认为，在不实践TDD的项目中(我自己所处的环境就是如此)，事后编写单元测试仍有着其合理性：<br />
&nbsp;&nbsp;&nbsp; 1. 以消极的态度来看，既然项目本身不严格要求事先编写单元测试，那么就可以在事后去做了。这至少比不去做要好，聊胜于无嘛。(嘿嘿，是够消极的，但也拿你没办法)<br />
&nbsp;&nbsp;&nbsp; 2. 事后编写单元测试至少也是一种检验手段，当然，肯定比不上事先编写的单元测试。因为，事后编写的单元测试很可能会"将就"已经写好的应用程序，正如John所说"事后编写的单元测试将是肤浅的，不会对代码进行良好地测试"。但...仍然是聊胜于无嘛 :-D (哈哈，有完没完了)<br />
&nbsp;&nbsp;&nbsp; 3. 可以把单元测试，其中就包含事后单元测试，作为"后来者"了解、学习应用程序的手段。因为单元测试程序就是应用程序的"客户"，所以无论它是事先写的，还是事后写的，都能够表现出应用程序的行为。<br />
&nbsp;&nbsp;&nbsp; 4. 事后单元测试，也可能转化为事先单元测试。在应用程序的整个生命周期中，维护阶段是最长的。在"漫长"的维护过程中，"之前"所写的"事后"单元测试将会成为"后来者"(包括原始作者本人)的"事先"单元测试。在改进程序的过程中，这些单元测试仍然能起到监督的作用。(orz，有点儿诡辩)<br />
&nbsp;&nbsp;&nbsp; 虽然，事后单元测试明显不如事先单元测试，但它的作用仍然不可低估。只要编写了优秀的单元测试程序，无论是在哪个阶段，它都会对改进应用程序有莫大的帮助。(这可不是"聊胜于无"能够表达的)<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/206812.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-06-09 20:55 <a href="http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rock Star 2008 -- Chet Haase(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/06/01/205180.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 01 Jun 2008 13:02:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/06/01/205180.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/205180.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/06/01/205180.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/205180.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/205180.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">Rock Star 2008 -- Chet Haase</span></span></strong><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这是一篇在今年的JavaOne会议期间<a href="http://java.sun.com/javaone/sf/2008/articles/rockstar_chethaase.jsp">对Chet Haase的访谈</a>。关注Swing，Java 2D的朋友，对这位图形/UI开发方面的大牛肯定不会陌生。很高兴在不久前由他与Romain Guy合著的<a href="http://www.china-pub.com/39895">Filthy Rich Clients一书的中文版</a>已经出版了^_^ (2008.06.01最后更新)<br />
&nbsp;&nbsp;&nbsp; 祝大家六.一国际儿童节愉快！愿大家都能保持一颗年轻的心*_*<br />
&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">特别祝地震灾区的小朋友们节日快乐，愿他们在今后的岁月中能够幸福地成长:-)<br />
</span><br />
简历：<a href="http://graphics-geek.blogspot.com/">Chet Haase</a>是Adobe公司Flex SDK组的高级计算机科学家，他关注于图形问题及特性。早些时候，他工作于Sun Microsystems公司，是Desktop Java组的一名客户端软件架构师，涉及形如Java 2D，Swing，AWT和部署这样的客户端技术。他与Romain Guy一起是<em>Filthy Rich Clients: Developing Animated and Graphical Effects for Desktop Java Applications</em>一书的合著者。在2008 JavaOne大会中，他和Guy有一个关于Filthy Rich Client的议题。<br />
<br />
</span><span style="font-size: 10pt;">Q：若干月前，你出版了<a href="http://www.filthyrichclients.org/">Filthy Rich Clients</a>一书。关于它，开发者们应该知道些什么？<br />
A：有四点：第一，它涉及的特性丰富的功能，能帮助开发者创造更好的应用和更好的用户体验。第二，它有许多关于Swing和Java 2D的内幕，以便开发者不仅能理解我们写的功能，还能理解这些技术本身，他们就能使用这些技术去写出他们自己的功能了。第三，它是一本有趣的读物，至少我们努力去做到这一点。最后，在<a href="http://www.filthyrichclients.org/">本书的站点</a>上，可获取所有的示例和工具类库，包括源代码。<br />
<br />
Q：在Sun的Java Client组工作了8年之后，你在2008年的1月份离开了，并加入了Adobe。告诉我们你所从事的工作。<br />
A：我加入Flex组，从事与图形有关的工作--这没什么奇怪的。在目前为止，我主要专注于相关的平台：Flex，Flash，AIR，MXML，ActionScript3。对于某个在过去8年多的时间中一直从事于Java方面工作的人来说，有很多东西需要学习。同时，在全组为下一版的发布而对某些组件进行重新构架的工作中，我已在帮忙了。开始时，我关注动画方面。<br />
<br />
Q：周三下午5点，在Metreon，有一个Adobe的活动。到时你会与James Ward有一个简短的演讲。告诉我们关于它的一些情况。<br />
A：那会很有趣。James Ward将给出Flex平台的一个简介，包含如何编写与Java后台交互的Flex客户端程序。而我--也可能是我的合著者Romain Guy--将展示一些"Filthy Rich" Flex应用示例。这些示例关于如何使用Flex去实现我们在书中谈到的功能。使用Flex去实现的方法会不同，但你可以使用该平台去达到非常相似的功能。任何感兴趣的人都应去Adobe的展台，然后获取一份邀请。<br />
<br />
Q：是什么吸引你去开发图形软件？<br />
A：我喜欢从我的程序中得到可视化的反馈。当它正常工作时，你可以在屏幕中看到结果。当它坏掉了，你也能看到它在屏幕中不正常的样子。与调试某些隐蔽的数据库事务相比，这更能让我满足。还有，图形能够将我的数学背景结合到我的软件背景中。计算机图形学使用了从线性代数到三角函数到几何学再到微积分学的许多技术。使用数学在屏幕上显示美妙的图片，与我所处的数学领域能做的多数事情相比，这更加有趣。<br />
<br />
Q：对于那些想以做图形软件作为职业的人，你有什么建议？<br />
A：应用你所需要的数学课程。并不需要高级的知识，但以我已做过的很多软件来看，我着重学习了线性代数，及其一定的微积分学。<br />
<br />
Q：对于新接触Java程序设计语言的程序员，你会给出什么建议？<br />
A：不使用行号。不要把你的整个应用放在一个方法中。相信垃圾收集器能做好它的本职工作。<br />
<br />
Q：哪种产业已经准备开始大规模履行在Web服务方面的承诺？<br />
A：摔跤。当WWF(世界摔跤联合会)在万维网(World Wilde&nbsp;Web)上使用Web服务时，网络别名就将是wwwwwfws。<br />
<br />
Q：能描述一下你写代码的过程吗？<br />
A：我会试着在脑海中收集素材，可能会将一些不成熟的想法写在小纸片上，这样我之后就不用再去找了。这一机制中的某些观念将来会用得上。然后我开始写原型。<br />
我在一些书中看过一种理想化的方法，你将整个系统--API，或随便什么--以简短方法和注释的形式进行描述，然后你所要做的就是填充细节。也许真的有人用这种方法写代码，但我尚未遇到。<br />
很多软件是研究性项目，在那里并不清楚什么是可能的或需要做的细节是怎样的。所以当我快要知道我想达到的什么时，我会开始写一些代码以找出接下来要做的工作以及如何去做。<br />
<br />
Q：你创造的最让你感自豪的代码是什么？<br />
A：<a href="https://timingframework.dev.java.net/">Timing Framework</a>。这个类库增补了一项我在Java SE中见到的空白--Java SE缺乏有用的定时-动画特性。然后基于我们自己的或其他人给我们的需求，Timing Frame有机地发展起来。<br />
我也对Filthy Rich Clients中的所有代码感到十分自豪。这些代码示例意在向人们展示如何去完成一些特定的任务，但像简单执行的代码一样，作为教程而写成的这些代码也便于教学。<br />
<br />
Q：下一次大的技术革命是什么？<br />
A：香蕉电话。水果兼具通信元件与营养小吃的功能。<br />
<br />
Q：在此之前的五年内，你认为最重要的Java API是什么？<br />
A：没有什么比System.out.println更有用的了？<br />
<br />
Q：你能给我们一个优美代码的例子吗？<br />
A：这儿就有一个：<br />
&nbsp; 10 print "fart"<br />
&nbsp; 20 goto 10<br />
<br />
Q：如果你想向Santa Claus要一个<a href="http://www.netbeans.org/">NetBeans IDE</a>的新插件，那会是什么？<br />
A：<a href="http://www.adobe.com/devnet/actionscript/articles/actionscript3_overview.html">ActionScript3</a>的编辑器，如何？<br />
<br />
Q：当你感到困惑时，你会做些什么？<br />
A：拿一支笔和一张纸，然后思考着这个问题。也会散散步，或者与某个人一起讨论这个问题，或者换一杯咖啡--也许没什么帮助，但味道不错。<br />
<br />
Q：Java平台最近的什么变化使你的生活变得轻松些？<br />
A：我发现<a href="http://java.sun.com/javase/downloads/index_jdk5.jsp">J2SE 5.0</a>的一些语言变化，像新的for循环，很有用。<br />
我用过一些泛型以简化Timing Framwork API，虽然我知道泛型的真正好处是让用户泛型化API，而不是让开发者在他们自己的代码中使用泛型。<br />
<br />
Q：Swing开发者需要理解的最重要的东西是...？<br />
A：在沾手之前，先关掉食品加工器。<br />
<br />
Q：你认为Web 2.0的最大技术障碍是什么？<br />
A：有太多的框架用于"Web 2.0"应用。特别是在Ajax世界，很难知道从哪儿开始、继续或结束。从某种程度来看，一个开发者的脑海中只会中意最有可能流行的框架。可能更应该写作"Web Too"，即，"太多的选择"。<br />
<br />
Q：你是否是网络开发者社区的一位正式成员？<br />
A：我并不处在某个特定的社区内，但在我关注的开发领域内，我是一位经常写作的<a href="http://graphics-geek.blogspot.com/">博客</a>。我认为拥有伟大的类库是吸引开发者群的重要组成部分。<br />
但是向开发者们解释应该如何使用这些类库，以及使用特定的、合适的例子去展示这一点，则是另一个同等重要的部分。<br />
<br />
Q：图形软件应该如何溶入Web 2.0的画卷中？<br />
A：就我理解，Web 2.0是关于在浏览器中的更好、更动态的用户体验。不论你使用何种框架，这种体验都意味着丰富的图形与功能。甚至只是简单的如，动态地将购物商品拖入购物车，或使用有趣的图形技术在页面上使某个元素淡入淡出，这样的功能。<br />
<br />
Q：你如何看待开源软件？<br />
A：我认为开源软件很伟大，但并不是由于人们可能设想到的原因。当人们听到一个项目是开源的，他们可能猜测有一大批来自世界各个角落的人们在帮助这个项目，向其中塞入代码，并以这种松散的方式进行协作。<br />
我见过的大多数开源项目已较少关心人人贡献了，而更多的是关心源码库的透明性以及来自开发者的广泛反馈。多数的公司和人可能更关心的是，他们能够看到黑箱中的东西，然后会建议进行适当的改进，而不会是实际地去做这些改进，也不会使自己亲自加入到项目中。<br />
所以，就像我的Timing Framwork所获得的好处，并不是大家直接地向它提交代码，而是大家能够去看它是如何工作的，然后与我讨论怎样去改进它，以去适应他们的特定情形。<br />
Java和Flex SDK都是开源。不论选择加入的人的水平如何，我希望它们都能从社区的参与中获益。<br />
<br />
Q：大家知道你在博客中写有滑稽和诙谐的东西。你是否认为幽默与滑稽能保持你作为一个开发者的敏锐、自由与创意。<br />
A：是的，就是那样。这就是我那样做的原因。<br />
不，我这么做其实也没有什么理由。我只是喜欢博得一笑罢了。然而我也知道，如果人们都睡着了，你就不能教他们了。<br />
所以，使一次讲演具有趣味性，与使其信息丰富一样重要。我见过十分乏味但内容很好的讲演，但一般来说，对于越活跃的讲演，大家越能记住它，也越想再听一次。<br />
这同样适用于文章：为了使你的观点具有穿越性，如果你能使用一些有趣的比喻或词汇，就比只是在他们面前弄一些代码和方程式，更有机会使你的读者们继续看下去。<br />
<br />
Q：能与我们分享两个你最喜欢的奇客<sup>[1]</sup>笑话或故事吗？<br />
A：我不能保证他们是我最喜欢的或是最好笑的，但至少它们都很新--在本周才把它们帖到我的博客中--也很奇客：<br />
&nbsp;&nbsp;What do you call it when a chip manufacturer loses money?<br />
&nbsp;&nbsp;A cash miss.<br />
<br />
&nbsp; What do you call someone that has to use a dull web application?<br />
&nbsp;&nbsp;Bored to tiers.<sup>[2]</sup>
<br />
<strong style="font-size: 12pt;">译注</strong><br />
[1]奇客，Geek，指性格奔放甚至怪僻，擅长计算机的人。<br />
[2]由于对这两则笑话的"本质"尚未领悟，所以暂不翻译。<br />
<br />
<strong><span style="font-size: 12pt;">请关注上一篇译文：<a href="http://www.blogjava.net/jiangshachina/archive/2008/05/31/204939.html"><span style="font-size: 10pt;"><strong></strong></span><strong>Rock Star 2008 -- Joshua Bloch</strong></a></span></strong></span><strong></strong>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/205180.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-06-01 21:02 <a href="http://www.blogjava.net/jiangshachina/archive/2008/06/01/205180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rock Star 2008 -- Joshua Bloch(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/05/31/204939.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 31 May 2008 15:39:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/05/31/204939.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/204939.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/05/31/204939.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/204939.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/204939.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 14pt;">Rock Star 2008 -- Joshua Bloch</span><br />
</strong></div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这是一篇在今年的JavaOne会议期间对<a href="http://java.sun.com/javaone/sf/2008/articles/rockstar_joshuabloch.jsp">Joshua Block的访谈</a>。作为闻名的Java guru，大家已经从Joshua的<a href="http://java.sun.com/docs/books/effective/"><em>Effective Java</em></a>，<em>Java Collection Framework</em>，...中获益了。相信，Joshua Block在访谈中的一些真知灼见，仍然会使大家受益良多。(2008.06.02最后更新)<br />
<br />
<strong>简历</strong>：Joshua Block，Google公司的首席Java构架师，曾为Sun Microsystems公司的杰出工程师，由于<em>Effective Java Programming Language Guide</em>一书，他获得了<em>Software Development Magazine</em>极富盛名的Jolt大奖。在Sun公司时，他领导设计并实现了众多的Java平台特性，包括JDK 5.0的语言升级以及获奖的Java集合框架。他拥有卡纳基-梅隆大学计算机科学博士学位，目前还是<em>Effective Java, Second Edition</em>的作者。<br />
<br />
Q：你和Brian Goetz将会有一个名为"撰写下一本伟大的Java技术书籍"(BOF-6588)的议题。写一本伟大的Java书籍需要做些什么？<br />
A：这要看实际情况。你可以以书籍的形式介绍那些之前还没有被介绍过的重要知识，就如Brian Goetz和Tim Peierls 在<em>Java Concurrency in Practice</em>一书中所做的那样。或者，你也可以写一部对现有资料的原创讲解，使它能被所有未接触过Java的人所接受，就如Kathy Sierra和Bert Bates在<em>Head First Java</em>一书中所做的那样。你也可以先弄一大堆资料，然后提炼出其中的精华，就像Peter Sestoft在<em>Java Precisely</em>一书中做的那么出色。这还可以接着说下去。<br />
Q：对于你写的<em>Effective Java</em>，开发者经常告诉我，那是他们最喜欢的Java技术书籍。你将二次贡献名为"More 'Effective Java'"(TS-6623)的议题，在那儿，你将谈论Java平台新近的最佳实践。让我们先了解一点儿它的情况。<br />
A：我正在收集新出现在该书第二版中的素材。去年，我花了大量时间在谈论泛型，所以今年我仍将重点关注它，我确实有一个新的期望能够分享的泛型小窍门。<br />
我也将介绍一些关于enum类型的有趣素材，并有一个针对延迟初始化的最佳实践的简明指导。是的，我还计划展示我们州长年轻时的一张祼照<sup>[1]</sup>。<br />
<br />
<strong style="font-size: 12pt;">一天的生活<br />
</strong>Q：描述一下你在Google的一天中的生活。<br />
A：那是个工作的好地方。我与许多来自世界各地的聪明人一起工作，那儿不缺乏挑战性的工作去做。 Google理解开源以及回馈社区的重要性，所以他们一直支持我在Java平台方面的持续工作。额外的好处就是他们因此而获得的好名声。<br />
Q：你在Google吃的最好的一餐是什么？<br />
A：嗯...可能是两年前在Cafe 7吃的烤鹌鹑？或者可能是在Pinxto吃的Shiro Maguro Sashimi？有些饮品也是很棒的。<br />
Q：这些食物真的很好吃吗？<br />
A：是的。我不是说在那儿我就没吃过差的食物，但总的来看，确实很好。<br />
<br />
<strong style="font-size: 12pt;">将"Bloch"作为动词</strong><br />
Q：如果你能将"Bloch"变成动词，就如已对"Google"做的那样，那么动词"Bloch"会是什么意思？<br />
A：天啊，这是个很难的问题。可能是"写出好的API"？当然，这将不会发生，而我也乐于接受。在每一代中，只会有很少的人够得上用一个词去称呼，并且经常是一个形容词。如你所知的：牛顿的，笛卡尔的，莎士比亚的，就像这样的。<br />
由于某种原因，这使我想起多年前看到的一则头条。当Miles Davis<sup>[2]</sup>逝世时，纽约时报上他的讣告说："Miles Davis，小号手，去逝了；爵士天才，65岁，绝对的酷"。我也想到了自己，"是的，这就是一个总结。而且还没人能上这样的头条"。<br />
<br />
<span style="font-size: 12pt;"><strong>最美的代码</strong></span><br />
Q：你所喜爱的代码是怎样的？或者说，你所见过的最漂亮的代码是什么样的？如诗般的代码？<br />
A：我看过许多代码都能激起这种反响。就像真的诗歌一样，在某人看来很美的一段代码可能在其它人看来很丑陋。这儿有一段神秘的代码诗：<br />
&nbsp;&nbsp;&nbsp; static int inverse(int val) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t *= 2 - val * t;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t *= 2 - val * t;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t *= 2 - val * t;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t *= 2 - val * t;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t;<br />
&nbsp;&nbsp;&nbsp; }<br />
正如它的名字所暗示的，该方法计算奇参数的乘法逆值，mod 2^32。换言之，对于所有的奇整数i，有i * inverse(i) == 1。它根据牛顿迭代原理。<br />
有严格的证据表明这四个迭代--方法中非回转的循环--对于任何输入值都足够了。那么这是我见过的最漂亮的代码吗？不，但它确实十分的聪明。<br />
如果你喜欢这样的东西，你应该去看看Henry S. Warren的书<em>Hacker's Delight</em>(Addison-Wesley，2003)。当你能看这本书时，要翻到"The Quest for an Accelerated Population Count"一章。在Osram和Wilson的<em>Beautiful Code</em>(O'Reilly，2007)一书中，当你手里也有这本书的话，也要读一下Jon Bentley 写的"The Most Beautiful Code I Never Wrote"一章。Jon写了许多诗歌般的代码。<br />
<br />
<strong style="font-size: 12pt;">最有趣的代码<br />
</strong>Q：你能想到的最有趣的代码是什么？<br />
A：<a href="http://thedailywtf.com/">The Daily WTF</a>中满是有趣的代码。Bill Pugh也经常发给我一些由<a href="http://findbugs.sourceforge.net/">FindBugs</a>发现的有趣代码。这儿有一个令人欣喜的例子--我发誓，它从未被透露过：<br />
&nbsp;&nbsp; public Object getObject(java.util.Map&lt;String,Class&lt;?&gt;&gt; map)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws SerialException<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map = new Hashtable(map);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!object.equals(null)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return map.get(object);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new SerialException("The object is not set");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
Q：代码是世界语吗？如果是，那为什么？<br />
A：我认为音乐才称得上，其次就是数学了。因为音乐将自然语言与数学符号联系了起来。代码很像数学：创建具有良好可读性程序的很大一部分工作就是选择好的标识符名称，而这就要基于自然语言。<br />
Q：哪位Java达人深深地影响了你？是在什么方面？<br />
A：应该是Doug Lea<sup>[3]</sup>。他知道很多事情中的很多东西，多年来，他激发了我数不清的想法。他总有让人感兴趣的事情可说。并且他有一种不可思异的能力，能发挥出VM的最佳性能。幸运的是，他已教给我一些他的窍门。例如，Branch Free代码会运行的很快，缓存的作用将会终结，内联探索法(I<font size="2">nlining Heuristics</font>)在性能方面有着巨大的效用。<br />
Q：能给我们一个你创造的最让你感到自豪的代码例子吗？并解释一下为什么？<br />
A：集合框架。虽远非完美，但多年来，已经证明了它的可维护性与优雅。而前面提到的Doug Lea构建的java.util.concurrent包中的许多部分就构建集合框架之上。并且在程序员的来信中，他们总是告诉我，集合框架是如何使他们的工作更加赏心悦目。它能使你写像下面这个程序那样的代码，这个程序通过标准输入流计算文件中所有的片语：<br />
public class Anagram {<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int minGroupSize = Integer.parseInt(args[0]);<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Read words from input and put into simulated multimap<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Map&lt;String, List&lt;String&gt;&gt; anagrams =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new HashMap&lt;String, List&lt;String&gt;&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Scanner s = new Scanner(System.in); s.hasNext(); ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String word = s.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String alphagram = alphagram(word);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List&lt;String&gt; group = anagrams.get(alphagram);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (group == null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; anagrams.put(alphagram, group = new ArrayList&lt;String&gt;());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; group.add(word);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print all permutation groups above size threshold<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (List&lt;String&gt; group : anagrams.values())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (group.size() &gt;= minGroupSize)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(group.size() + ": " + group);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private static String alphagram(String s) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] chars = s.toCharArray();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Arrays.sort(chars);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return String.valueOf(chars);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
Q：当你感到困惑时，你会做些什么？<br />
A：我会喝一杯美味的热咖啡。Google有很好的咖啡机，这些机器来自于Barefoot Coffee Roasters。如果不管用，我就会去走一走。如果还不管用，我就会去叫Doug Lea。<br />
Q：你使用哪些网络资源，以紧跟Java技术。<br />
A：规范，还有Google。无论走到哪里，这都能帮上我。<br />
Q：是否有一种益智的训练或有趣的行为让你觉得能使你成为一名更好的开发者？<br />
A：我认为数学和写作能使你成为更好的开发者。数学与编程一样，要求严谨的思维。而写作会强迫你去组织你的想法。数学和写作都训练了相同的审美机能，而这对于写出好的程序也是必需的。<br />
Q：你希望在工科学校中你还应该学到哪些东西？<br />
A：我希望我能更好地学会弹吉他，现在我弹的可不好。我希望能学一门外语，还有艺术史。当然，还想学一些商业和金融方面的知识。这样说起来，哥伦比亚大学会对我有更好的帮助。我会鼓励本科生们，当他们还有时间时，就要创造机会去获取更宽泛的知识。以后，他们会有足够的时间深入到其中。<br />
<br />
<strong style="font-size: 12pt;">译注</strong><br />
[1]这张"祼照"就是加利福尼亚州现任州长</span><span style="font-size: 10pt;">阿诺.施瓦辛格的健美祼肤照。</span><br />
<span style="font-size: 10pt;">[2]Miles Davis，爵士乐大师。点击<a href="http://www.nytimes.com/learning/general/onthisday/bday/0525.html">此处</a>可以看到纽约时报上关于他的讣告全文。<br />
[3]<a href="http://g.oswego.edu/">Doug Lea</a>，java.util.concurrent的创始人，目前也是<a href="http://openjdk.java.net/">OpenJDK</a>临时管理委员会的成员。<br />
<br />
<strong style="font-size: 12pt;">请关注下一篇译文：<a href="http://www.blogjava.net/jiangshachina/archive/2008/06/01/205180.html">Rock Start 2008 -- Chet Haase</a></strong></span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/204939.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 23:39 <a href="http://www.blogjava.net/jiangshachina/archive/2008/05/31/204939.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对象。<br />
import java.util.*;<br />
import java.util.concurrent.*;<br />
<br />
public class CallableExample {<br />
<br />
&nbsp;&nbsp;&nbsp; public static class WordLengthCallable<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; implements Callable {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String word;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public WordLengthCallable(String word) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.word = word;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Integer call() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return Integer.valueOf(word.length());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String args[]) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExecutorService pool = Executors.newFixedThreadPool(3);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Set&lt;Future&lt;Integer&gt;&gt; set = new HashSet&lt;Future&amp;lg;Integer&gt;&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (String word: args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Callable&lt;Integer&gt; callable = new WordLengthCallable(word);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Future&lt;Integer&gt; future = pool.submit(callable);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set.add(future);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int sum = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Future&lt;Integer&gt; future : set) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum += future.get();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.printf("The sum of lengths is %s%n", sum);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.exit(sum);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
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><item><title>字符串排序(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/04/07/190912.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 07 Apr 2008 01:10:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/04/07/190912.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/190912.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/04/07/190912.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/190912.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/190912.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 14pt;">字符串排序</span></strong><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文是Sun官方以Blog形式发布的Java核心技术窍门(<a href="http://blogs.sun.com/CoreJavaTechTips/">JavaCoreTechTip</a>)中的一个。我之前尚未关注过java.text.Collator类，看过<a href="http://blogs.sun.com/CoreJavaTechTips/entry/sorting_strings"><span style="font-size: 10pt;">Sorting Strings</span></a></span><span style="font-size: 10pt;">这个tip之后觉得有些意义，故翻译在了此处，也希望对其它朋友有所助益。(2008.04.07最后更新)</span><br />
<br />
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">使用Java平台进行字符串排序被认为是一件简单的工作，但为国际市场开发程序时，则需要有更多的考虑。如果你陷入只关注英语的心态中，并认为你的程序会工作的很好，因为它所显示的字符串从今往后都是一样的，你可能认为一切都很正常。但一旦你有一位西班牙用户，他希望能够正常地对ma&#241;ana进行排序，但如果你都是使用String类中缺省的compare方法去做排序，字符&#241;将会跟在字符z之后，而在正常的西班牙语排序中，&#241;应该在字符n和o之间。这就是java.text包的类Collator发挥作用的地方了。
<br />
想像这样的一组词
<ul>
    <li> first
    </li>
    <li> ma&#241;ana
    </li>
    <li> man
    </li>
    <li> many
    </li>
    <li> maxi
    </li>
    <li> next
    </li>
</ul>
使用String类的默认排序机制，即它的compare()方法，排序的结果将会是：
<ul>
    <li> first
    </li>
    <li> man
    </li>
    <li> many
    </li>
    <li> maxi
    </li>
    <li> ma&#241;ana
    </li>
    <li> next
    </li>
</ul>
&nbsp;&nbsp;&nbsp; 此处，ma&#241;ana出现在maxi与next之间。而在西班牙语世界中，ma&#241;ana应该出现在many和maxi之间，
因为在字母表中，字符&#241;(读作e&#241;e)跟在n之后。当来了一个德语用户，想用他们自己的变音符时，你就可以写一个自己的定制排序规则来处理&#241;，
否则一组使用fa&#231;ade的设计模式将会怎样呢？你是否想让fa&#231;ade出现在factory之前或之后呢？(关键是如同对c或其它字符那样去处理&#231;的小写变音符。)
<br />
&nbsp;&nbsp;&nbsp; 这就是类Collator能派上用场的地方了。类Collator用于对语言敏感的排序问题，并不会只基于它们的ASCII/Unicode字符去尝试排序。
使用Collator要求你在完全应用它的特性之前要理解一个额外的属性，即称之为强度(Strength)的属性。Collator的强度设置决定了在排序时如何使用强(或弱)匹配。
该属性有4个可能的值：PRIMARY，SECONDARY，TERTIARY和IDENTICAL。具体是哪个强度在产生作用取决于语言环境。
典型地，会有如下的情况。按从后往前的顺序，IDENTICAL强度表示能够被进行相同的处理的字符必须是一致的。TERTIARY通常用于忽略大小写差异。SECONDARY用于忽略变音符，如n和&#241;。
PRIMARY与IDENTICAL相似也是基于字母之间差异，但是当处理控制字符和发音时还是有所不同。查看<code>Collator</code>的javadoc，
以获取更多关于这些强度之间的差异及分解(Decomposition)模式规则的信息。
<br />
&nbsp;&nbsp;&nbsp; 为了使用Collator，你需要先得到它的一个实例。你既可以调用getInstance方法以得到一个针对默认语言环境的Collator对象，
也可以传递一个指定的Locale对象给getInstance方法以得到一个针对特定语言环境的Collator对象。例如，为了获得针对一个西班牙语的
Collator对象，你应使用new Locale("es")去创建一个西班牙语的Locale对象，然后将它传入getInstance方法中：<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);">Collator&nbsp;esCollator&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;&nbsp;&nbsp;Collator.getInstance(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;Locale(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">es</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">));</span></div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 假设针对该语言环境的默认Collator强度，针对西班牙语的默认强度是SECONDARY已经足够了。然后你将这个Collator对象如任一Comparator对象
那样传入Collections类的sort方法的比较规则参数中，以得到排序后的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);">Collections.sort(list,&nbsp;esCollator);</span></div>
<span style="font-size: 10pt;">操作之前的单词列表，你现在就会得到一个基于西班牙语字母表的恰当排序结果：
<ul>
    <li> first
    </li>
    <li> man
    </li>
    <li> many
    </li>
    <li> ma&#241;ana
    </li>
    <li> maxi
    </li>
    <li> next
    </li>
</ul>
如果你在上述Collator中换用US的Locale对象，由于&#241;并不是US中本有的字母，所以ma&#241;ana将会出现在man和many之间。
<br />
这儿有一个简洁的例子以显示这些差异。
import java.awt.*;<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.text.</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;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;java.util.List;&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;Explicit&nbsp;import&nbsp;required</span><span style="color: rgb(0, 128, 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 />
</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;Sort&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;Runnable&nbsp;runner&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;Runnable()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;run()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;words[]&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, 0);">"</span><span style="color: rgb(0, 0, 0);">first</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">ma&#241;ana</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">man</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;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">many</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">maxi</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">next</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;List&nbsp;list&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;Arrays.asList(words);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JFrame&nbsp;frame&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;JFrame(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Sorting</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;frame.setDefaultCloseOperation&nbsp;(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Box&nbsp;box&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;Box.createVerticalBox();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setContentPane(box);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JLabel&nbsp;label&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;JLabel(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Word&nbsp;List:</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;box.add(label);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JTextArea&nbsp;textArea&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;JTextArea(&nbsp;list.toString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;box.add(textArea);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collections.sort(list);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label&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;JLabel(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Sorted&nbsp;Word&nbsp;List:</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;box.add(label);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textArea&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;JTextArea(list.toString&nbsp;());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;box.add(textArea);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collator&nbsp;esCollator&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;Collator.getInstance(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;Locale(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">es</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;Collections.sort(list,&nbsp;esCollator);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label&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;JLabel(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Collated&nbsp;Word&nbsp;List:</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;box.add(label);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textArea&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;JTextArea(list.toString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;box.add(textArea);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setSize(</span><span style="color: rgb(0, 0, 0);">400</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">200</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;frame.setVisible(</span><span style="color: rgb(0, 0, 255);">true</span><span style="color: rgb(0, 0, 0);">);<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;EventQueue.invokeLater&nbsp;(runner);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><string></string><br />
<center>
<img src="http://blogs.sun.com/CoreJavaTechTips/resource/sorting.png" alt="" />
</center>&nbsp;&nbsp;&nbsp; 最后还有一点儿关于语言排序规则的信息。通过调用getInstance方法而得到的Collator对象通常是支持特定语言的RuleBasedCollator实例。
你可使用RuleBasedCollator去定义你自己的排序顺序。该类的Javadoc更完整地描述了这种规则的语法，但还是让我们先假设你有一个4字符字母表，
并希望字母的顺序是CAFE，而不是ACEF，你的规则看起来就像这样：</span><br />
<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);">String&nbsp;rule&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&lt;&nbsp;c,&nbsp;C&nbsp;&lt;&nbsp;a,&nbsp;A&nbsp;&lt;&nbsp;f,&nbsp;F&nbsp;&lt;&nbsp;e,&nbsp;E</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">;<br />
RuleBasedCollator&nbsp;collator&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;RuleBasedCollator(rule);</span></div>
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">上述规则通过展示不同字母的大小写定义了特定的字母顺序为cafe。现在对单词列表ace，cafe，ef和face使用新的规则进行排序，
排序结果的顺序为cafe，ace，face和ef：<string><br />
</string>
</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.text.</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;java.util.</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;Rule&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;</span><span style="color: rgb(0, 0, 255);">throws</span><span style="color: rgb(0, 0, 0);">&nbsp;ParseException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;words[]&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, 0);">"</span><span style="color: rgb(0, 0, 0);">ace</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cafe</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">ef</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">face</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;String&nbsp;rule&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&lt;&nbsp;c,&nbsp;C&nbsp;&lt;&nbsp;a,&nbsp;A&nbsp;&lt;&nbsp;f,&nbsp;F&nbsp;&lt;&nbsp;e,&nbsp;E</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;RuleBasedCollator&nbsp;collator&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;RuleBasedCollator(rule);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;list&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;Arrays.asList(words);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collections.sort(list,&nbsp;collator);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(list);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">在对上述代码编译并运行之后，你将看到使用新规则排序后的单词：<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);">&gt;&nbsp;javac&nbsp;Rule.java<br />
&gt;&nbsp;java&nbsp;Rule<br />
</span><span style="color: rgb(128, 0, 0); font-weight: bold;">[</span><span style="color: rgb(128, 0, 0);">cafe,&nbsp;ace,&nbsp;face,&nbsp;ef</span><span style="color: rgb(128, 0, 0); font-weight: bold;">]</span></div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt;">请以后阅读Javadoc中更多的关于
规则语法的信息，再尝试扩展字母表并处理不同的变音符。<br />
&nbsp;&nbsp;&nbsp; 现在，当你为全世界开发
程序时，你的程序就能做出更好的准备以去适应本地用户了。也要确保字符串在资源包中，如之前的一个窍门所展示的那样：<span style="font-size: 10pt;">Earlier tip。(译注：原文并没有提供这个Earlier tip的正确链接地址。)</span></span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/190912.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-04-07 09:10 <a href="http://www.blogjava.net/jiangshachina/archive/2008/04/07/190912.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Applet不适用于这样的应用？(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/02/26/182208.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 26 Feb 2008 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/02/26/182208.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/182208.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/02/26/182208.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/182208.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/182208.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">Java Applet不适用于这样的应用？</span></span></strong><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在基于浏览器的应用中，为何Flash大大流行于Java Applet?Java Applet是否真的不适用于这样的应用场景？<a href="http://weblogs.java.net/blog/joconner/archive/2008/02/java_applets_no.html">John O'Conner有话要说</a>...(2008.03.29最后更新)</span></span><br />
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 我已正与一个公司一起创建一个聊天系统，以帮助该公司能够在线地销售出它们更多的产品。后端全是Java，还有Spring，Servlet，...都是些好东西。前端是...好吧，是Flash。<br />
&nbsp;&nbsp;&nbsp; 当有可能时，我更喜欢在每个地方都使用Java，故我就这个面向客户的应用--一个真实的聊天客户端程序选择使用Flash而提出了一些问题。这个谈话进行起来就像这样：<br />
&nbsp;&nbsp;&nbsp; <strong><em>John</em></strong>：哇，这个产品在每处都使用了Java，但前端除外，而这正是客户与之实际交互的地方。为什么在那儿选择Flash？<br />
&nbsp;&nbsp;&nbsp; <strong><em>Dev mgr</em></strong>：是的，Flash很容易。使用这样一个简单的UI也是很容易的。<br />
&nbsp;&nbsp;&nbsp; <strong><em>John</em></strong>：我明白，但使用Java也可十分简单地做出这个聊天UI。现在，Swing的布局管理器已很容易使用，特别是当你使用像NetBeans这样的IED来布局UI时。<br />
&nbsp;&nbsp;&nbsp; <strong><em>Dev mgr</em></strong>：那可能是对的，但这种选择不仅仅是基于简易地创建UI。<br />
&nbsp;&nbsp;&nbsp; <strong><em>John</em></strong>：哦？那是什么呢？<br />
&nbsp;&nbsp;&nbsp; <strong><em>Dev mgr</em></strong>：每个客户端浏览器中都有Flash。我们不必再安装一个运行时程序了。<br />
&nbsp;&nbsp;&nbsp; <strong><em>John</em></strong>：Java也将会那样，不是吗？大部分的PC制造商都包含它了。<br />
&nbsp;&nbsp;&nbsp; <strong><em>Dev mgr</em></strong>：也许，但我们知道Flash肯定是那样的。Flash遍及网络，而且很可能客户已经有了Flash的运行时程序。如里没有的话，下载与安装这个运行时程序也是很快、很容易的。<br />
&nbsp;&nbsp;&nbsp; <strong><em>John</em></strong>：是的，但Java也将易于安装。<br />
&nbsp;&nbsp;&nbsp; <strong><em>Dev mgr</em></strong>：但是Java会花费很长的安装时间。它太大了。<br />
&nbsp;&nbsp;&nbsp; <strong><em>John</em></strong>：我不知道现在是否还是那样。为了使运行时程序更小也更加的模块化，已经做了很多工作。所以你仅需要下载你所需要的部分。<br />
&nbsp;&nbsp;&nbsp; <strong><em>Dev mgr</em></strong>：我们所需要的就是非常、非常的简单。一个非常简单的UI，它有着非常简单的功能去中转聊天文本。Flash可以做得很好。<br />
&nbsp;&nbsp;&nbsp; 交谈继续着，但我不想弄得太长。可能Flash做小应用时会比Java做的更好？在Sun工作了如此长的时间，我就很自然地想像Java无处不在，而且其它的每个人也都喜欢它。我一直都能把事情做好...但很显然，不是每个人都能做到。下载文件的大小从未烦扰过我...但它确实烦扰到了其它的一些人。<br />
&nbsp;&nbsp;&nbsp; 另外，我发现感觉与现实一样重要。所以，如果Java现在更快，更轻巧，而且在浏览器中工作得极好，那将会怎样呢？不知何故，这还是没有用。旧有的感觉仍就在那儿--Java很慢，很大，而且很难在浏览器中工作。更令人惊讶的是，你会发现有些人在那种情况下就再也不把Java当作他们的解决方案了。<br />
&nbsp;&nbsp;&nbsp; 我联系了一些朋友，他们一起搭建面向客户的网站。他们也是使用Flash。那么Java呢？当然...都是在后端的业务逻辑中。那么对于客户将会使用到的应用呢？如果这些应用是嵌入到浏览器中的，它们就会使用Flash。哇!<br />
&nbsp;&nbsp;&nbsp; 这样，你如何对待这种感觉呢？Java需要做些什么以反击这些旧有的感觉？为什么Flash在基于浏览器的应用中更为流行？我们如何才能使那些应用转而使用Java？<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/182208.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-02-26 14:07 <a href="http://www.blogjava.net/jiangshachina/archive/2008/02/26/182208.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入db4o(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/12/05/164430.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 05 Dec 2007 04:31:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/12/05/164430.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/164430.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/12/05/164430.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/164430.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/164430.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">深入db4o<span style="font-size: 10pt;"><span style="font-size: 8pt;"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;"><span style="font-size: 10pt;"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;"><span style="font-size: 10pt;"><span style="font-size: 8pt;"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;"><span style="font-size: 10pt;"><span style="font-size: 10pt;"><span style="font-size: 12pt;"><span style="font-size: 10pt;"><br />
</span></span></span></span></span></span></strong></span></span></span></span></strong></span></span></span></strong></span></span></span></span></strong>
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这是Rick Grehan发表在</span><span style="font-size: 10pt;"><a href="http://www.theserverside.com/">TheServerSide</a>上的<a href="http://www.theserverside.com/tt/articles/article.tss?l=Insidedb4o"><span style="font-size: 10pt;">一篇关于面向对象数据库--db4o</span><span style="font-size: 10pt;">的文章</span></a></span><span style="font-size: 10pt;">，较全面地介绍了<a href="http://www.db4o.com/">db4o</a>的关键特性，希望对大家认识<a href="http://www.db4o.com/">db4o</a>能有所帮助。(2007.12.07最后更新)</span><span style="font-size: 10pt;"><br />
<br />
&nbsp;&nbsp;&nbsp; db4o-<em>针对对象的数据库</em>-是一个完全的对象数据库；它以使对象在其生命周期中-无论是在数据库内或是在外-都保持着它们的本性这样一种方式操纵对象。不论类的复杂性如何，对象的内容，结构和关系都能够被保存。<br />
&nbsp;&nbsp;&nbsp; 更准确地说，db4o是一个数据库引擎，你只要将它的一个jar文件包含到你的数据库应用的类路径中就可以使用它了(至少对于Java是这样的)。所以，db4o运行在与你的应用程序相同的进程空间中，并能被直接地调用；它不需要类似于在ODBC或JDBC中使用的驱动文件。db4o存在针对Java，.NET和Mono的版本；它们在功能上都彼此相等。(事实上，使用.NET创建的db4o数据库也能由Java程序访问；反之亦然。)<br />
&nbsp;&nbsp;&nbsp; db4o是开源的。可执行文件，源代码和文档可从http://www.db4objects.com/中下载。广泛的例子程序，和一个活跃的用户社区一样，也都可以从这个站点中找到。<br />
&nbsp;&nbsp;&nbsp; db4o最引人的特性之一就是它在简易性与强大的功能之间的显著平衡。一方面，它的API是如此地易于掌握和方便使用，即使是初学者也能在相同的时间内创建一个功能完备的数据库对象。另一方面，这些相同的API也提供了更底层的能够深入调用数据库引擎的方法，以允许核心开发者为了得到适当的性能而能深入到该引擎的内部中去调整db4o的工具。<br />
&nbsp;&nbsp;&nbsp; db4o的特性就是最好的证明--这胜过只是讨论--所以我们将通过示例这种方法去证明db4o。然而，我们必须牢记本文通篇只是展示了db4o特性中的一部分罢了。感兴趣的朋友会发现为了知晓该数据库引擎的全部功能而去查阅db4o的文档所花的时间是值得的。<br />
<br />
<strong><span style="font-size: 12pt;">
db4o基础</span></strong><br />
&nbsp;&nbsp;&nbsp; 让我们以初学者使用db4o时可能会做的事情开始：定义了一些类，然后持久化这些类的对象。我们所假定的类为同样也是假定的QA项目做一个跟踪测试的系统。我们的系统由两个类组成，首先是TestSuite类：<br />
&nbsp;&nbsp;&nbsp; public class TestSuite {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String name;&nbsp; // Test Suite name<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String description;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String configuration;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private char overallScore;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private ArrayList &lt;TestCase&gt; cases;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private long dateExec;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... &lt;remainder of TestSuite definition&gt; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; TestSuite是TestCase对象的容器，(一个测试用例就是一个可单独执行的测试程序--相关的测试用例组合成一个测试组)。测试组使用额外的，全局的数据成员，每个数据成员的用途也是相对明显的：configuration记录被测试的指定系统；overallScore是对整个测试组一个简要的评分('P'代表通过，'F'代表失败，'B'代表被阻塞，等等。)；dateExec是一个毫秒级的域，标识该测试组被执行时的日期与时刻。是ArrayList对象的cases含有单个的测试用例，由TestCase类定义：<br />
&nbsp;&nbsp;&nbsp; public class TestCase {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String comment;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private char status;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private long duration;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private float result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... &lt;remainder of TestCase definition&gt; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 每个测试用例都有一个名称，形式自由的注释字段，状态(通过或失败)，持续时间和结果(例如，为了与测试-字节/秒的吞吐量-的任意数据关联)。<br />
&nbsp;&nbsp;&nbsp; 因为我们所关注是db4o的使用，所以我们不想在描述这些类的使用细节上停留。就让我们简单地说，我们已经执行了一个特别地测试组中所有的测试用例，将测试的结果存放在一个TestSuite对象中(与TestCase对象相关的ArrayList对象cases中)，然后关闭数据库。是不是太容易了。<br />
&nbsp;&nbsp;&nbsp; // Create the database<br />
&nbsp;&nbsp;&nbsp; new File("testsuites.YAP").delete();<br />
&nbsp;&nbsp;&nbsp; ObjectContainer db = Db4o.openFile("testsuites.YAP");<br />
&nbsp;&nbsp;&nbsp; // Store the TestSuite object<br />
&nbsp;&nbsp;&nbsp; db.set(testsuite);<br />
&nbsp;&nbsp;&nbsp; // Close the database<br />
&nbsp;&nbsp;&nbsp; db.close();<br />
&nbsp;&nbsp;&nbsp; 就是它。弹指一挥间，你就已经做到了。(当然，为了保持简洁，我们去掉了创建TestSuite对象和它的TestCase组件的细节)<br />
&nbsp;&nbsp;&nbsp; 停下来想想上述代码做了什么事情。特别要考虑你没有看到的--db4o已经做了但还未被告之的事情。<br />
&nbsp;&nbsp;&nbsp; 首先，我们不需要告诉db4o任何关于TestSuite类的结构的信息；不需要我们的帮助，db4o就能发现这个结构。利用Java反射机制的能力，db4o测定TestSuite类的结构，并勾勒出该类的装配方式以推导出此类对象的成员与关键数据。<br />
&nbsp;&nbsp;&nbsp; 第二，我们不必建议db4o去关注ArrayList。不仅我们不必将ArrayList的大小告诉db4o，而且我们也不必把它里面的内容告诉db4o。正如db4o在它刚接触testsuite对象时就能够发现它所需要的一切，db4o也能知道它所需要的关于(在ArrayList中的)TestCase对象的所有信息。<br />
&nbsp;&nbsp;&nbsp; 结果就是，如果我们把testsuite作为一个任意宠大而复杂的对象树的根，db4o能找到并存储整个树而不需要我们的任何协助。所以，存储这个处于根部的对象testsuite也就是存储了整个ArrayList对象。<br />
&nbsp;&nbsp;&nbsp; 最后，我们也没有必须要求db4o以事务的保护性方式去调用set方法。任何会修改ObjectContainer(表示数据库的db4o对象)的调用都会自动地开启一个事务，除非已经有一个活跃的事务了。此外还会调用close方法去终止这个事务，所以上述代码等价于：<br />
&nbsp;&nbsp;&nbsp; db.startTransaction();<br />
&nbsp;&nbsp;&nbsp; db.set(testsuite);<br />
&nbsp;&nbsp;&nbsp; db.commitTransaction();<br />
&nbsp;&nbsp;&nbsp; db.close();<br />
&nbsp;&nbsp;&nbsp; 此处的startTransaction和commitTransaction方法是为了证明我们的观点而虚构的。db4o也确实有显示地提交或中止事务的方法，但为了使原先的代码足够的简洁我们没有使用这些方法。db4o隐式的事务使得数据库能够一直处于一致的状态；一旦commit方法已经执行了，数据库的完整性就能够得到保证，甚至是发生了灾难性失败也能如此。<br />
<br />
<strong><span style="font-size: 12pt;">
查询I - QBE</span></strong><br />
&nbsp;&nbsp;&nbsp; 有了已经存于数据库中的对象，下一步我们想要展示的操作将肯定就是查询和恢复。db4o提供了三种查询API：有一种简单，有一种优雅，还有一种则复杂。每一种API都有其所长，并适用于不同的查询条件。以db4o的眼光来看，你选择哪一种API并没有关系：它们都是可兼容的。<br />
&nbsp;&nbsp;&nbsp; 我们以简单的API开始：query by exampel(QBE)。<br />
&nbsp;&nbsp;&nbsp; 使用QBE是如此的容易：为你的查询目标构建一个'模板'对象，然后把它传入ObjectContainer的query方法。实际上，你就是告诉db4o'去拿到所有与这个对象看起来一样的对象'。(这与JavaSpaces查询API非常相似；为了清楚地了解如何处理基本数据类型，可以看下面的内容，db4o的处理方式与JavaSpaces不同。也要注意，JavaSpace Entry对象期望使用public字段，db4o则没有这种要求。)<br />
&nbsp;&nbsp;&nbsp; 假设一个测试组名为"Network Throughput"，我们想取出这个测试组执行的所有测试以便我们能确定失败了的测试所占的百分比(基于TestSuite的overalScore值)。使用QBE，完成该工作的代码如下：<br />
&nbsp;&nbsp;&nbsp; // Open the database<br />
&nbsp;&nbsp;&nbsp; ObjectContainer db = Db4o.openFile("testsuites.YAP");<br />
<br />
&nbsp;&nbsp;&nbsp; // Instantiate the template object, filling in<br />
&nbsp;&nbsp;&nbsp; // only the name field<br />
&nbsp;&nbsp;&nbsp; testTemplate = new TestSuite("Network Throughput");<br />
<br />
&nbsp;&nbsp;&nbsp; // Execute the query<br />
&nbsp;&nbsp;&nbsp; ObjectSet result = db.get(testTemplate);<br />
&nbsp;&nbsp;&nbsp; fails = 0.0f;<br />
&nbsp;&nbsp;&nbsp; total = 0.0f;<br />
<br />
&nbsp;&nbsp;&nbsp; // Step through results,<br />
&nbsp;&nbsp;&nbsp; while(result.hasNext())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testsuite = (TestSuite)result.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(testsuite.getOverallScore()=='F')<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fails += 1.0f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; total += 1.0f;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; if(total == 0.0f)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("No tests of that type found.");<br />
&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Percent failed: " + (fails/total * 100.0f) + "%");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Total executed: " + total);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; db.close();<br />
&nbsp;&nbsp;&nbsp; 在上面的代码中，testTemplate是QBE的模板对象。注意，只有它的name字段有真实的值；所有其它的成员变量不是为null就是为0。Null或0字段不参与QBE查询；因此，调用db.get方法就会返回在该数据库中name字段匹配"Network Throughput"的所有TestSuite对象。匹配的TestSuite对象将返回在一个ObjectSet结果对象中。上述代码遍历该结果，取出对象，然后计算结果并展示出来。<br />
&nbsp;&nbsp;&nbsp; QBE明显的优点就是它的简易性。不需要掌握其它单独的查询语言。另外，QBE也是类型安全的：你不需要创建一个类似于SQL的查询语句<br />
&nbsp;&nbsp;&nbsp; SELECT TestSuite.overallScore FROM TestSuite WHERE TestSuite.name = 200.0<br />
&nbsp;&nbsp;&nbsp; 另一方面，由于该查询是由Java代码创建的，编译器不会允许你把一个浮点值赋给一个String字段；反之亦然。<br />
&nbsp;&nbsp;&nbsp; QBE明显的缺点就是它只能执行"等于"查询。另外，QBE使用null值去确定不参与查询的String或对象引用成员变量，使用0值去指定不参与查询的数字字段。所以，例如，我不能发明一个QBE查询去获得所有result字段的值为0的TestCase对象。<br />
&nbsp;&nbsp;&nbsp; 更为精细的查询要求一个能力更强的查询机制，而db4o恰恰就有这样一种机制。<br />
<br />
<span style="font-size: 10pt;"><span style="font-size: 10pt;">
</span><strong><span style="font-size: 12pt;">
查询方式II - 原生查询(Native Query)</span></strong><br />
&nbsp;&nbsp;&nbsp; db4o的原生查询系统应该是能想像得到的最具弹性的查询机制。不像使用数据库查询语言去构建查询，你是使用"无格式的普通Java语句"去构造原生查询。原生查询用两种手段去实现这件不可思意的工作：一个是Predicate类；另一个是QueryComparator接口。这个类包含一个可重载的(overrideable)回调方法，该方法将指定如何从数据库中选择对象(如果你愿意，你将会看到查询语句的主体....)。这个接口只声明了一个方法，该方法用于指明如何对查询结果进行排序。<br />
&nbsp;&nbsp;&nbsp; 作为一个例子，我们假设想找到在给定的一周内执行过了的总得分为"failed"，但与之关联的测试用例中有超过一半的被评为"passed"的测试组。这不是一个简单的"等于"查询，所以它不能使用QBE构建。<br />
&nbsp;&nbsp;&nbsp; 然而，db4o的原生查询可以直接地生成该查询。首先，我们继承db4o的Predicate类：<br />
&nbsp;&nbsp;&nbsp; // Predicate class sub-class for native query example<br />
&nbsp;&nbsp;&nbsp; public class NativeQueryQuery extends Predicate&lt;TestSuite&gt;<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectContainer db;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private long startdate;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private long enddate;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 构造器要在本地获得ObjectContainer对象和日期范围<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public NativeQueryQuery(ObjectContainer _db,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long _start, long _end)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; db = _db;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startdate = _start;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enddate = _end;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 这就是查询的主体<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean match(TestSuite testsuite)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float passed;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float total;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestCase testcase;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 判断testsuite是否在指定的日期范围内<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(testsuite.getDateExec()&lt;startdate ||<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testsuite.getDateExec()&gt;enddate) return false;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 如果该测试组对象中没有测试用例对象，则拒绝将该测试组对象放入查询结果中<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(testsuite.getNumberOfCases()==0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 检查该测试组对象中的测试用例的通过率是否超过50%<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; passed = 0.0f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; total = 0.0f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;testsuite.getNumberOfCases(); i++)<br />
&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; testcase = testsuite.getTestCase(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(testcase.getStatus()=='P')<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; passed+=1.0f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; total+=1.0f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((passed/total)&lt;.5) return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 注意在这个类的使用中使用了Java泛型语义，这就是告诉db4o只去取TestSuite对象。当查询执行时，TestSuite对象就会传入match方法(我们之前提到过的回调方法)，如果传入的TestSuite对象符合查询规范该方法就返回true，否则就返回false。<br />
&nbsp;&nbsp;&nbsp; match方法中的代码首先确定侯选对象是否是在一周的日期范围内。如果是，则循环该对象中的成员变量测试用例的对象，计算出所有通过了的测试用例的总数。如果，得到的通过率小于50%，该测试组就被拒绝；否则，就让它通过。<br />
&nbsp;&nbsp;&nbsp; 我们可以使用如下的代码准确地展示该查询程序：<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; TestSuite testsuite;<br />
&nbsp;&nbsp;&nbsp; NativeQueryQuery nqqClass;<br />
&nbsp;&nbsp;&nbsp; Date now;<br />
<br />
&nbsp;&nbsp;&nbsp; // Open the database<br />
&nbsp;&nbsp;&nbsp; ObjectContainer db = Db4o.openFile("testsuites.YAP");<br />
<br />
&nbsp;&nbsp;&nbsp; // Instantiate a NativeQueryQuery object,<br />
&nbsp;&nbsp;&nbsp; // setting the start and end dates for<br />
&nbsp;&nbsp;&nbsp; // any test in the past week<br />
&nbsp;&nbsp;&nbsp; // 604800000 = milliseconds in a week<br />
&nbsp;&nbsp;&nbsp; now = new Date();<br />
&nbsp;&nbsp;&nbsp; nqqClass = new NativeQueryQuery(db,<br />
&nbsp;&nbsp;&nbsp; now.getTime()-604800000L,<br />
&nbsp;&nbsp;&nbsp; now.getTime());<br />
<br />
&nbsp;&nbsp;&nbsp; // Execute the query and display the<br />
&nbsp;&nbsp;&nbsp; // results<br />
&nbsp;&nbsp;&nbsp; System.out.println("Results:");<br />
&nbsp;&nbsp;&nbsp; ObjectSet results = db.query(nqqClass);<br />
&nbsp;&nbsp;&nbsp; if(results.isEmpty())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("&nbsp; NOTHING TO DISPLAY");<br />
<br />
&nbsp;&nbsp;&nbsp; while(results.hasNext())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testsuite = (TestSuite)(results.next());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(testsuite.toString());<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; db.close();<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; 可以把原生查询想像成这样：目标类的对象一个接一个从数据库中取出，然后把它们传入match方法中。只有那些被match方法返回true的对象才会置于查询结果ObjectSet对象中。基本上可以说，如果你会知道如何写Java代码，那么你就知道如何写原生查询。<br />
&nbsp;&nbsp;&nbsp; 那么排序呢？如果想按日期的升序排列查询结果，我们就要实现QueryComparator接口，如下所示：<br />
&nbsp;&nbsp;&nbsp;
public class NativeQuerySort implements QueryComparator&lt;TestSuite&gt;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int compare(TestSuite t1, TestSuite t2)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (t1.getDateExec() &lt; t2.getDateExec()) return -1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (t1.getDateExec() &gt; t2.getDateExec()) return 1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;
}<br />
compare方法的作用十分明显。那些在查询中得以胜出的对象会成对的传入compare方法，如果第一个对象会排在第二个对象之前，相同或之后的位置，该方法就会分别返回一个小于，等于或大于0的值。为了准确地说明对查询结果的排序，我们实例化NativeQuerySort，并把对query方法的调用修改成如下：<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; // Instantiate the sort class<br />
&nbsp;&nbsp;&nbsp; nqsClass = new NativeQuerySort();<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; ObjectSet results = db.query(nqqClass, nqsClass);<br />
&nbsp;&nbsp;&nbsp; . . .<br />
其它的代码仍然与原先的保持一致。<br />
&nbsp;&nbsp;&nbsp; 好怀疑的读者可能会抱怨道，原生查询只是一种编程上的小伎俩--相比较于直接去拿到所有的TestSuite对象然后再排除其中不符合条件的对象这样的程序，原生查询并不快。<br />
&nbsp;&nbsp;&nbsp;
是的，但并不十分准确。原生能够被优化。你所需要做的只是把两个jar文件--db4o-xxx-nqopt.jar(xxx表示db4o的版本)和bloat.jar--置于CLASSPATH环境变量中。在查询执行的时候，这些类库中的代码会对(在match方法中)例如基本数据类型比较，算术和布尔表达式，简单的对象成员访问，以及更多方面的结构进行优化。这个被支持的优化的列表在不停的增长，因为db4o引擎还在扩展优化的范围。<br />
<br />
<strong><span style="font-size: 12pt;">
查询方式III - S.O.D.A.</span></strong><br />
&nbsp;&nbsp;&nbsp; db4o独一无二的能力之一就是它的API被分层了。开发者能够选择通过高层次--赋予数据库引擎相当大的自由度，让它决定如何去实现它的操作--或者开发者也可以使用一种更直接地方式去访问db4o。后一种选择为程序员平添了更多的负担，程序员必须更加小心地引导数据库引擎的内部工作。但回报就是得到一个更快，能力更强的数据库应用。<br />
&nbsp;&nbsp;&nbsp; db4o的S.O.D.A.(Simple Object Data Access)查询机制就是该层次API的一个完美的例子。S.O.D.A.就是db4o的内部查询系统--QBE和原生查询都被翻译成了S.O.D.A.。然而，应用程序也能直接地调用S.O.D.A.。<br />
&nbsp;&nbsp;&nbsp; 假设我们想找到所有名为"Network Throughput"，且至少拥有一个其result字段--我们使用这个参数作为字节/秒的量度--不小于指定值(比方说，100)的测试用例的测试组。为该请求而做的S.O.D.A.查询可能就像这样：<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; TestSuite testsuite;<br />
<br />
&nbsp;&nbsp;&nbsp; // Open the database<br />
&nbsp;&nbsp;&nbsp; ObjectContainer db = Db4o.openFile("testsuites.YAP");<br />
<br />
&nbsp;&nbsp;&nbsp; // Construct the query<br />
&nbsp;&nbsp;&nbsp; Query query = db.query();<br />
&nbsp;&nbsp;&nbsp; query.constrain(TestSuite.class);<br />
&nbsp;&nbsp;&nbsp; Constraint nameConst = query.descend("name").<br />
&nbsp;&nbsp;&nbsp; constrain("Network Throughput");<br />
&nbsp;&nbsp;&nbsp; query.descend("cases").descend("result").<br />
&nbsp;&nbsp;&nbsp; constrain(100.0f).smaller().and(nameConst);<br />
<br />
&nbsp;&nbsp;&nbsp; System.out.println("Results:");<br />
&nbsp;&nbsp;&nbsp; // Execute the query<br />
&nbsp;&nbsp;&nbsp; ObjectSet result = query.execute();<br />
&nbsp;&nbsp;&nbsp; if(result.isEmpty())<br />
&nbsp;&nbsp;&nbsp; System.out.println("NOTHING TO DISPLAY");<br />
<br />
&nbsp;&nbsp;&nbsp; while(result.hasNext())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testsuite = (TestSuite)(result.next());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(testsuite.toString());<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; db.close();<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; 在由Illustration 1所示图表的帮助下，这些有点儿神秘的代码就变得不那么神秘了。该程序所构建的归总起来就是一个用于指导底层数据库引擎的查询图(Query Graph)。descend方法创建了该图的一个分支，该分支向下步入对象的结构中。每个descend方法就在这个树中构建一个结点，可以在这些结点上再附上一个约束(使用constrain方法)。用SQL的话来说，约束指定了查询的"WHERE"子句部分。多个约束可以在与(and)或或(or)方法的协助下结合起来。在上面的查询中我们已经使用了and方法去关联这些约束。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/clip_image002.gif" alt="" border="0" /><br />
与其它的查询方式一样，查询结果返回到ObjectSet对象中，通过遍历该对象就可取出那些拿到的对象。<br />
&nbsp;&nbsp;&nbsp; 注意，由于S.O.D.A.是一种低层次的访问方法，没有智能的指示，它就没有默认的行为。访问cases对象的成员变量result字段的代码很简单<br />
&nbsp;&nbsp;&nbsp; query.descend("cases").descend("result"). ...<br />
我们并没有告诉S.O.D.A."cases"是一个集合对象。所以当查询执行时，它会不被察觉地检测ArrayList对象cases中所有元素(TestCase对象)的result字段，然后会正确地返回那些拥有符合搜索规范的测试用例的测试组。<br />
<br />
</span></span><span style="font-size: 10pt;"><strong><span style="font-size: 12pt;">db4o性能调优</span></strong><br />
&nbsp;&nbsp;&nbsp; 我们已经展示了db4o的基本操作(但无关紧要的删除操作除外，下面将会提到它)。但，正如我们在本文通篇所提到的，db4o发布(expose)了一个API层次结构，以允许开发者能够选择以何种层次的API去控制建立在该数据库引擎之上的应用程序。从另一方面看，如果你所想做的只是向数据库存入，及从数据库取出对象，那么你就已经看到了你所需要的一切。然而，如果你的应用的需求超出了添加，更新，查询和删除，可能还有一个db4o的特性可解决你的问题。<br />
&nbsp;&nbsp;&nbsp; db4o的ObjectContainer实际上发布(expose)了两个API。第一个API非常的简单，由十个方法组成。这些方法处理数据库的打开与关闭；添加，更新，查询和删除对象；及提交或中止事务。短言之，该API为你提供了在操纵数据库时所需要所有功能。然而，该API中的一个方法--ext()--是进入"被扩展的"ObjectContainer的一个入口。该被扩展的ObjectContainer为深入控制db4o的内部发布(expose)了更多方法。例如，你可以获得并改变数据库的配置上下文，使用它你能够修改该引擎的行为。<br />
&nbsp;&nbsp;&nbsp; 例如，假设你已经从数据库中拿到了一个TestSuite对象，发现该对象中的数据是错误的，并决定该对象应该被删除。此外，你还决定你必须删除的不仅是该TestSuite对象，而且还有所有与之关联的TestCase对象(在ArrayList对象cases中)。<br />
&nbsp;&nbsp;&nbsp; 你是可以冗长而乏味地遍历这个ArrayList对象，一个接一个地删除每一个TestCase对象，然后再删除TestSuite对象本身。可能一种更好的解决方案是为这个TestSuite对象启用db4o的"级联删除"特性。<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; // Fetch the database's configuration context<br />
&nbsp;&nbsp;&nbsp; Configuration config = db.ext().configure();<br />
&nbsp;&nbsp;&nbsp; // Get the ObjectClass for TestSuite<br />
&nbsp;&nbsp;&nbsp; ObjectClass oc = config.objectClass("testsuites.TestSuite");<br />
&nbsp;&nbsp;&nbsp; // Turn on cascaded delete<br />
&nbsp;&nbsp;&nbsp; oc.cascadeOnDelete(true);<br />
&nbsp;&nbsp;&nbsp; ...&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; db.delete(ts1);<br />
&nbsp;&nbsp;&nbsp; . . .<br />
在上述代码中，我们实例化了一个ObjectClass对象，该对象使我们能够访问到TestSuite对象的db4o内部表现形式。我们打开cascadeOnDelete标记，以便当执行db4o.delete(ts1)时，不仅ts1对象会被删除，而且所有由ts1引用的TestCase对象也会被删除。(由于显而易见的原因，默认情况下，级联删除是被关闭的)<br />
&nbsp;&nbsp;&nbsp; 作为另一个例子，假设你想为数据库预分配存储空间，以至于要最小化磁盘驱动器的头移动(head movement)。(最好是在硬盘碎片整理之后，并新创建一个数据库时这样做。)并且假设以ObjectContainer对象db作为打开了的数据库：<br />
&nbsp;&nbsp;&nbsp; // Fetch the database's configuration context<br />
&nbsp;&nbsp;&nbsp; // 获得数据库的配置上下文<br />
&nbsp;&nbsp;&nbsp; Configuration config = db.ext().configure();<br />
&nbsp;&nbsp;&nbsp; // Pre-allocate 200,000 bytes<br />
&nbsp;&nbsp;&nbsp; // 预分配200000000字节<br />
&nbsp;&nbsp;&nbsp; config.reserveStorageSpace(200000000L);<br />
把数据库文件预扩展到200000000字节(略小于200兆字节)。假设该磁盘已经做碎片整理了，那么被分配的块就是连续的，这可显著提升数据库的访问。<br />
<br />
<strong><span style="font-size: 12pt;">db4o高级应用</span></strong><br />
&nbsp;&nbsp;&nbsp; 完全可以说，db4o在它不大的空间(约500K)内已装入了足够多的特性，相比较于db4o在运行过程中所做的众多事情，我们不能花费更多的笔墨去解释它们了。但是有两个特性十分突出，以至于肯定要提到它们。<br />
&nbsp;&nbsp;&nbsp; db4o的对象复制实现了被总称为面向对象版的数据库同步。使用复制所提供的功能，你能为一个数据库中的一个对象做一个副本，并将该副本放入另一个数据库中。使用这样的方法，副本对象就无形中和原始对象关联在了一起。对任一对象--原始对象或副本对象--的改变都会被跟踪到，以便在之后的某个时候，数据库能够被重组，并且这两个数据库中对象的不同之处可被分解(例如，可同步这两个数据库)。<br />
&nbsp;&nbsp;&nbsp; 它工作起来就像这样：为使一个数据库可被复制，与事务计数器一样，该数据库中被创建的任何一个对象都用一个唯一全局标识符(UUID)进行了标记。当你从原始数据库中"复制"一个对象到另一个数据库中，副本对象会带着与它的原始对象相同的UUID和事务计数器。副本数据库现在就可以从它的原始数据库那儿弄走了。修改副本对象的内容将会导致对象的事务计数器被修改。所以，当这两个数据库重新连接起来，db4o内建的同步处理机制就能一个对象一个对象地进行正确的匹配(使用UUID)，并确定原始或副本对象是否已经改变了。db4o甚至能追踪到每个对象发生最后一次修改时的时间，以便用户写的冲突解决代码能确定哪个对象是最近更新的。<br />
&nbsp;&nbsp;&nbsp; 从操作行为来看，db4o的同步处理机制与原生查询十分相似。回想一下，当实现了一个原生查询类时，我们要定义一个match方法，该方法确定哪些对象符合(或不符合)查询规范。使用同步复制，我们要定义一个ReplicationProcess对象，我们会把冲突处理对象传入该方法中。这些Java代码可能像这样：<br />
&nbsp;&nbsp;&nbsp; . . .<br />
&nbsp;&nbsp;&nbsp; ReplicationProcess replication = db1.ext().<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replicationBegin(db2, new ReplicationConflictHandler()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Object resolveConflict(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReplicationProcess rprocess, Object a, Object b)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; return winning_object;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />
在上述代码中，Object a是来自于数据库db1的对象，Object b则来自于数据库db2。默认情况下，同步是双向的。同步处理保证胜出的对象(由resolveConflict方法返回的对象)能存入这两个数据库中。所以当复制完成时，这两个数据库中被复制的对象就同步了。<br />
&nbsp;&nbsp;&nbsp; 最后，db4o最强大的特性之一就是它能毫不费力地容忍类结构的演变。假设，在向数据库内加入数百个TestSuite对象之后，我们决定这个类必须要被修改。就是说，我们已经被告之，该系统必须能追踪到每个TestSuite的执行QA工程师，所以必须加入如下字段<br />
&nbsp;&nbsp;&nbsp; private int engineerID;<br />
到TestSuite类的定义中。<br />
&nbsp;&nbsp;&nbsp; 现在我们就遇到了两个相关联的问题。第一个问题不是很糟糕：已存在于数据库中的用于表示测试的TestSuite对象，并没有为它们记录QA工程师的ID，所以我们将不得不将一个虚拟的值赋给这些对象的engineerID字段；该值会指出"未记录QA工程师的ID"。第二个问题就更难应付了：我们必须不知原因地把已有的TestSuite对象移植到"新的"类结构中。我们必须为数据库中所有已存在的TestSuite对象加上一个engineerID字段。除了把旧数据库中的对象复制到一个中间性的文件，然后重新创建一个数据库这种方法之外，我们还能怎么做呢？<br />
&nbsp;&nbsp;&nbsp; 幸运地是，使用db4o，我们确实什么都不用做。为了能操纵新的engineerID字段，以完成业务逻辑上所要求的变化，如果就只是向(我们的应用程序中的)TestSuite类添加一个engineerID字段，我们完全不必触动db4o API的任何调用。当db4o使用"新的"TestSuite类结构去读取"旧的"TestSuite对象，db4o将认为这些对象中的engineerID字段消失了，并且会优雅地把该字段的值设为0。如果我们把0当作"未记录QA工程师的ID"，那么我们所做的移植就完成了。写入到数据库中的新TestSuite对象将会包括新字段。(事实上，对旧的TestSuite对象本身进行重写，会使db4o不被察觉地为这些对象加上新字段。)所以，通过发布一个包含新的TestSuite定义的更新应用，我们完全可以不被察觉地从旧的TestSuite对象移植到新的...正如前述应用所做的那样。<br />
<br />
<strong><span style="font-size: 12pt;">全方位数据库</span></strong><br />
&nbsp;&nbsp;&nbsp; 通过适当地应用，db4o就能成为数据库中的"瑞士军刀"。它占用足够小的内存空间，使它能够被包含在一个不需要消耗大量资源的项目中。同样地，一个数据库只在磁盘上占用一个文件的事实可能会使人们在第一眼看到它时，不能认识到它丰富的功能。将数据库从一个地方移到另一个地方就是一个简单的文件拷贝；你不必担心分离的索引文件，数据文件，数据库结构文件等等这些文件的位置。对于快速部署和零管理的数据库应用，db4o很能胜任。<br />
&nbsp;&nbsp;&nbsp; 另外，根据我们已多次描述过的，db4o在简单性和优雅之间达到了适度的平衡。db4o的QBE既如此的简单，又如此的功能强大，对于一组令人惊奇的应用，它经常是我们唯一需要的查询API。如果你主要是通过指针导航而不是查询去访问数据库，QBE就特别有吸引力。在这种情况下，QBE经常能高效地拿到一个对象网络(Object Network)的根对象。然后，你就能使用db4o的激活(activation)功能从根部向下进行对象引用导航，如果这些对象都完全在内存中的话，你就更可以这么做了。<br />
&nbsp;&nbsp;&nbsp; 而在使用QBE并不高效的时候，原生查询和S.O.D.A.就能派上用场了，并且它们伴随着一堆特性和低层次的API。我们还没有展示db4o的加密功能，插入式文件I/O(例如，它允许你添加"写后读(read-after-write)"验证)，信号量，客户端/服务器端模式，以及其它难以计数的功能。我们结论性的建议很简单：当你的下一个Java应用需要一个数据库，在最终开始编码之前，你可以去访问一下http://www.db4objects.com/。这是很值得的。<br />
</span><br />
<img src ="http://www.blogjava.net/jiangshachina/aggbug/164430.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> 2007-12-05 12:31 <a href="http://www.blogjava.net/jiangshachina/archive/2007/12/05/164430.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Tutorials -- Concurrency(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/10/28/156522.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 28 Oct 2007 11:51:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/10/28/156522.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/156522.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/10/28/156522.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/156522.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/156522.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java Tutorials -- Concurrency&nbsp;&nbsp;&nbsp; 近一段时间在使用Thinking in Java(4th, English)和Java Concurrency in Practice学习Java并发编程。不得不说官方的Java Tutorias是很好的Java并发编程入门级教程，故将它其中的Concurrency一章翻译在了此处。与我翻译Ja...&nbsp;&nbsp;<a href='http://www.blogjava.net/jiangshachina/archive/2007/10/28/156522.html'>阅读全文</a><img src ="http://www.blogjava.net/jiangshachina/aggbug/156522.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> 2007-10-28 19:51 <a href="http://www.blogjava.net/jiangshachina/archive/2007/10/28/156522.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>"Java"的由来(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/09/04/142528.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 04 Sep 2007 02:20:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/09/04/142528.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/142528.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/09/04/142528.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/142528.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/142528.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 14pt;"><span style="font-size: 12pt;"><span style="font-size: 14pt;">"Java"的由来</span></span></span></strong><br />
</div>
<span style="font-size: 10pt;">在SUN CEO <a href="http://blogs.sun.com/jonathan/">Jonathan Schwartz</a>的<a href="http://blogs.sun.com/jonathan/entry/better_is_always_different">最新Blog</a>中，<a href="http://blogs.sun.com/jag/">Games Gosling</a>介绍了Java程序设计语言的名称--"Java"的由来。(2007.12.07最后更新)<br />
<br />
这个故事就像下面这样：<br />
&nbsp;&nbsp;&nbsp; 我们需要一个名字。我们当时已正在使用"oak"(这个词实际上是我随便挑的)了，当整个团队开始认同它时，商标律师们却否决了它。我们有大量的邮件在讨论关于</span><span style="font-size: 10pt;">名称</span><span style="font-size: 10pt;">的事情，但什么都没被解决。我们最终傻到要将第一个停在我们面前的东西作为这个</span><span style="font-size: 10pt;">名称</span><span style="font-size: 10pt;">。<br />
&nbsp;&nbsp;&nbsp; 我们的市场部门领导知道某个是"命名顾问"的人(我不记得他的名字了，但他很棒)。我们负担不起一般的产品命名过程所需要的金钱和时间。他同意做一些不奇特但高效且快速的事情：在那个将我们自己大概10多个人锁在一个房间里整个下午的会议上，他表现得如同一个推进器。他开始就问我们一些像"这个东西让你感觉如何？"(兴奋)这样的问题，"还有什么让你也有那种感觉?"(Java!)。我们最后在一个板子上写上了实际上是随机产生的词。然后他让我们做了一个排序的过程，最终得到了那些名字的一个排名列表。我们得到了10多个侯选名字，并将它们传给了律师们：律师们从上至下地研究这个列表，直到他们找到一个词使他们能够不再继续搜寻。"Java"是那个列表中的第四个词。该列表中的第一个词是"Silk"，我讨厌但其它的每个人都喜欢这个词。我最喜欢的是"Lyric"，是该列表中的第三个词，但它没能通过律师们的测试。我不记得其它的侯选名字都些是什么了。<br />
&nbsp;&nbsp;&nbsp; 那么是谁起的Java这个名字呢？市场部门组织了那次会议，那个顾问主持了它，我们中的一大堆人随口叫出了许多词。我不能真地肯定是谁第一个说出了"Java"，但我几乎可以肯定那个人就是Mark Opperman。<br />
&nbsp;&nbsp;&nbsp; 但可以肯定地是，那时没有任何睿智的市场人员经过通盘考虑之后才得到Java这个名字。</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/142528.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> 2007-09-04 10:20 <a href="http://www.blogjava.net/jiangshachina/archive/2007/09/04/142528.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Tutorials -- Generics(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/06/20/125293.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 20 Jun 2007 03:11:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/06/20/125293.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/125293.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/06/20/125293.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/125293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/125293.html</trackback:ping><description><![CDATA[<div style="text-align: center;"><span style="font-size: 12pt; font-weight: bold;"><span style="font-size: 14pt;">Java Tutorials -- Generics</span></span><br />
</div>
<span style="font-size: 10pt;">Java Generics伴随JDK 5.0发布到现在已经超过2年半了，但目前还没有被"非常广泛"地应用，我也一直没有进行过系统的学习。最近使用Thinking in Java(4th)和<a href="http://java.sun.com/docs/books/tutorial/">Java Tutorials</a>对泛型进行了专门的学习。</span><span style="font-size: 10pt;">本文是对Java Tutorials中<a href="http://java.sun.com/docs/books/tutorial/extra/generics/index.html">Generics一章</a>的翻译。其实关于Java Generics的文章已是汗牛充栋，之所以将这篇译文放在此处，也算是对自己学习的一种鼓励吧。该文的读者应该只有我一人，但仍然希望对其他朋友有所助益。</span><span style="font-size: 10pt;">(2007.07.10最后更新)<span style="color: red;"><br />
</span><br />
<span style="font-weight: bold;">1 介绍</span><br />
&nbsp;&nbsp;&nbsp; JDK 5.0引进了几种Java程序设计语言的新扩展。其中之一，就是对泛型的引入。<br />
&nbsp;&nbsp;&nbsp; 本次体验只是对泛型的介绍。你可能通过其它的语言，特别是C++ Template，已经对泛型的结构有些熟悉了。如果是这样的话，你将看到它们的相似点和重要的不同点。如果你对从别处看到的这种似曾相识的结构不熟悉的话，那就更好了，你可以从头开始，以避免不得不忘却一些误解。<br />
&nbsp;&nbsp;&nbsp; 泛型允许你抽象出类型。最普通的例子就是容器类型，如集合框架(Collection)中的那些类。<br />
&nbsp;&nbsp;&nbsp; 下面是一个此类特性的典型使用：<br />
&nbsp;&nbsp;&nbsp; List myIntList = new LinkedList(); // 1<br />
&nbsp;&nbsp;&nbsp; myIntList.add(new Integer(0)); // 2<br />
&nbsp;&nbsp;&nbsp; Integer x = (Integer) myIntList.iterator().next();&nbsp; // 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp; 第三行的强制类型转换有点烦人。基本上，程序员知道到底是什么类型的数据被放到这个特定的List中了。然而，这个强制类型转换是必需的。编译器只能保证迭代器将返回的是一个对象。为了确保一个类型为Integer的变量x是类型安全的，这个强制类型转换是需要的。<br />
&nbsp;&nbsp;&nbsp; 当然，这个强制类型转换并不会造成混乱。它仍然可能会造成一个运行时错误，可能是由程序员的失误而产生的。<br />
&nbsp;&nbsp;&nbsp; 那么程序员如何才能准确地表达他们的本意，使得一个List被限制为只能包含某个特定类型的数据呢？这正是泛型背后的核心思想。下面的程序片断是前述例子的泛型版：<br />
&nbsp;&nbsp;&nbsp; List&lt;Integer&gt; myIntList = new LinkedList&lt;Integer&gt;(); // 1'<br />
&nbsp;&nbsp;&nbsp; myIntList.add(new Integer(0)); // 2'<br />
&nbsp;&nbsp;&nbsp; Integer x = myIntList.iterator().next(); // 3'<br />
注意变量myIntList的类型声明。它不是指定了一个任意的List，而是指定了一个Integer对象的List，写作List&lt;Integer&gt;。我们说，List是一个拥有类型参数，在此处就是Integer，的泛型接口。当创建这个List对象时，我们也指定了一个类型参数。<br />
&nbsp;&nbsp;&nbsp; 再次注意，原来行3的的强制类型转换已经不需要了。<br />
&nbsp;&nbsp;&nbsp; 现在你可能会想我们所已经完成的就是移除了那个混乱(强制类型转换)。我们在行1处就使Integer成为一个类型参数，而不是在行3处进行强制类型转换。这儿就有一个很大的不同。在编译时，编译器就能够检查程序中的类型是否正确。当我们说myIntList在声明时使用了类型List&lt;Integer&gt;，那么就是告诉我们myIntList变量在任何时间和任何地点所包含的类型必须是Integer，并且编译器会确保这一点。相反地，强制类型转换只是告诉我们在代码中的某个独立的地方程序员所期望的情况而以。<br />
&nbsp;&nbsp;&nbsp; 在实际情况下，特别是在大型应用中，泛型可以提高程序的可读性和鲁棒性。<br />
<br />
<span style="font-weight: bold;">2 定义简单的泛型</span><br />
&nbsp;&nbsp;&nbsp; 下面是java.util包中List和Iterator接口定义的简短摘要：<br />
&nbsp;&nbsp;&nbsp; public interface List &lt;E&gt;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void add(E x);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator&lt;E&gt; iterator();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public interface Iterator&lt;E&gt;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean hasNext();<br />
&nbsp;&nbsp;&nbsp; }<br />
除了角括号中的内容，我们对这段代码应该比较熟悉了。这些是List和Iterator接口的形式类型参数的声明。<br />
&nbsp;&nbsp;&nbsp; 类型参数的使用可以贯穿于整个泛型声明，用在那些你以后想使用普通类型的地方(但有一些重要的约束，详见"良好的打印"一节)。<br />
&nbsp;&nbsp;&nbsp; 在"介绍"一节中，我们知道了使用了泛型类型声明的List接口的调用方法，如List&lt;Integer&gt;。在这个调用(一般就是调用一个参数化的类型)中，所有形式类型参数(即此处的E)出现的地方都被实际的类型参数(即此处的Integer)替换了。<br />
&nbsp;&nbsp;&nbsp; 你可能会想像List&lt;Integer&gt;表示一种由Integer统一地代替E之后的新的List版本：<br />
&nbsp;&nbsp;&nbsp; public interface IntegerList {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void add(Integer x);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator&lt;Integer&gt; iterator();<br />
&nbsp;&nbsp;&nbsp; }<br />
这种直觉是有助益的，但那也是误解。<br />
&nbsp;&nbsp;&nbsp; 说它是有助益的，是因为参数类型List&lt;Integer&gt;实际上所使用的方法看起来就是像那种扩展。<br />
&nbsp;&nbsp;&nbsp; 说它是误解，是因为泛型的声明确实没有用那种方式进行扩展。并不存在那些代码的多个复本，在源文件中、二进制文件中、硬盘中、内存中都没有这些复本。如果你是C++程序员，你将会发现这与C++ Template非常的不同。<br />
&nbsp;&nbsp;&nbsp; 泛型类型的声明绝对只会被编译一次，然后进入一个class文件中，就像一个普通的类或接口声明一样。<br />
&nbsp;&nbsp;&nbsp; 类型参数类似于方法或构造器中的普通参数。它非常像一个方法拥有一个形式值参数，这个参数描述了可以出现在该处的值的类型，泛型声明也有一个形式类型参数。当一个方法被调用时，一个真实的的参数会替换形式参数，然后这个方法会进行评估。当一个泛型声明被调用时，一个真实的类型参数也会替代形式类型参数。<br />
&nbsp;&nbsp;&nbsp; 需要注重一个命名规范。我们推荐你使用叫起来尽量精简的(如果可能的话，最好是单个字母)的名字作为形式类型参数。最好避免使用小写字母，这样就可以很容易地从普通的类和接口中区分出形式类型参数。如上述例子中，很多容器类型使用E代表容器中的元素(element)。<br />
<br />
<span style="font-weight: bold;">3 泛型与子类</span><br />
&nbsp;&nbsp;&nbsp; 让我们测试一下你对泛型的理解。下面的代码片断是合法的吗？<br />
&nbsp;&nbsp;&nbsp; List&lt;String&gt; ls = new ArrayList&lt;String&gt;(); // 1<br />
&nbsp;&nbsp;&nbsp; List&lt;Object&gt; lo = ls; // 2<br />
&nbsp;&nbsp;&nbsp; 第一行肯定是合法的。这个问题狡猾的部分是在第二行。这个问题可归结为：一个String对象的List也是Object对象的List吗？大部分人都会本能的回答到，是的！<br />
&nbsp;&nbsp;&nbsp; 那好，来看看下面几行：<br />
&nbsp;&nbsp;&nbsp; lo.add(new Object()); // 3<br />
&nbsp;&nbsp;&nbsp; String s = ls.get(0); // 4: 试图将一个Object对象赋值给一个String变量！<br />
&nbsp;&nbsp;&nbsp; 此处我们已经别名化了ls和lo。通过别名lo访问ls，一个String对象的List，我们可以向其中插入任意对象。但ls不能包含除String对象外的其它对象，则当我们试图从中获得些什么(Object对象)时，我们会感到非常的惊讶。<br />
&nbsp;&nbsp;&nbsp; 译者：上面这段话的意思是说，如果上述4行代码都成立的话，那么就会使我们感到很惊讶、很困惑。lo的类型是List&lt;Object&gt;，那么可以放入任意的Object到这个List中；而ls的类型是List&lt;String&gt;，即只能放入String对象。但lo引用的对象实际上是ArrayList&lt;String&gt;的对象，即只能存放String对象，所以上面的例子会使人感到很困惑。<br />
&nbsp;&nbsp;&nbsp; 当然，Java编译器会阻止这一切的发生--第二行将会导致一个编译时错误。<br />
&nbsp;&nbsp;&nbsp; 一般地，如果Foo是Bar的子类型(子类或子接口)，且G是某个泛型类型声明，那么G&lt;Foo&gt;并不是G&lt;Bar&gt;的子类型。这可能是当你学习泛型时所遇到的最困难的问题，因为这违反了我们根深蒂固的直觉。<br />
&nbsp;&nbsp;&nbsp; 我们不能假设集成对象们不会改变。我们的直觉可能会导致我们静态地思考这些问题。<br />
&nbsp;&nbsp;&nbsp; 例如，如果机动车管理部(Department of Motor Vehicles, DMV)向人口调查局(Census Bureau)提交了一组司机的名单，这会被看成是合理的，因为我们认为List&lt;Driver&gt;是List&lt;Person&gt;的子类型(假设Driver是Person的子类型)。实际上被提交的只是司机注册表的副本。否则，人口调查局也可以把那些不是司机的人也加入到这个名单 (List)中，这就会破坏DMV的记录。<br />
&nbsp;&nbsp;&nbsp; 为了应对这种情况，有必要考虑更为弹性的泛型类型。我们到目前为止所看到的规则实在是太具限制性了。<br />
<br />
<span style="font-weight: bold;">4 通配符</span><br />
&nbsp;&nbsp;&nbsp; 考虑这样一个问题，写一个程序打印出一个集合对象中的所有元素。下面的程序可能是你用老版Java语言所写的：<br />
&nbsp;&nbsp;&nbsp; void printCollection(Collection c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator i = c.iterator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (k = 0; k &lt; c.size(); k++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(i.next());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 这儿有一个不成熟的对泛型应用的尝试(并且使用了新的foreach循环语法)：<br />
&nbsp;&nbsp;&nbsp; void printCollection(Collection&lt;Object&gt; c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Object e : c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 这个问题就是新版的程序并不比旧版的程序更有用。反之，旧版的程序能够作为参数被任何类型的集合对象调用，新版的程序只能用于Collection&lt;Object&gt;，而这种情况已经被我们证明了，它并不是所有集合类型的超类。<br />
&nbsp;&nbsp;&nbsp; 那么什么才是所有集合对象的超类呢？它应该写作Collection&lt;?&gt;(叫作"collection of unknow，未知的集合")，这种集合类型的元素才可能配置任何类型。很明显，它被称作通配符类型。我们可以这样写：<br />
&nbsp;&nbsp;&nbsp; void printCollection(Collection&lt;?&gt; c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Object e : c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 然后我们就可以用任何集合类型来调用这个方法了。注意printCollection方法的内部，我们仍然可以从c中读取它的元素，并可将这些元素赋值给Object类型的变量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;?&gt; c = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(new Object()); // Compile time error<br />
&nbsp;&nbsp;&nbsp; 由于不知道c中元素的类型是什么，我们不能向它里面添加元素。add方法接受类型E的参数，即这个集合对象元素的类型。当然实际的类型参数是"?"时，它表示某个未知的类型。任何我们要添加入的参数都将不得不是未知类型的子类型。由于我们不知道这个类型是什么，所以我们不能传入任何类型。唯一的例外是 "null"，null是每个类型的成员(译者：null是每种类型的子类型。)。<br />
&nbsp;&nbsp;&nbsp; 另一方面，给出一个List&lt;?&gt;，我们就能调用get方法并使用得到的结果。所得结果的类型是未知的，但我们总可以知道它是一个 Object对象。因此将由get方法得到的结果赋予一个Object类型的变量，或是将它作为一个参数传入一个期望获得Object类型对象的地方，都是完全的。<br />
&nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">有边界的通配符</span><br />
&nbsp;&nbsp;&nbsp; 考虑这样的一个简单的绘图程序，它可以绘制诸如矩形和环形之类的形状。为了使用程序来描述这些形状，你可能是会下面那样定义一组类：<br />
&nbsp;&nbsp;&nbsp; public abstract class Shape {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public abstract void draw(Canvas c);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Circle extends Shape {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int x, y, radius;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void draw(Canvas c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Rectangle extends Shape {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int x, y, width, height;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void draw(Canvas c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 这些类可以被绘在一个画布(canvas)上：<br />
&nbsp;&nbsp;&nbsp; public class Canvas {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void draw(Shape s) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.draw(this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 任何绘制动作通常都会包含一组形状。假设使用List来表示它们，那么为方便起见，Canvas需要有一个方法去绘制所有的形状：<br />
&nbsp;&nbsp;&nbsp; public void drawAll(List&lt;Shape&gt; shapes) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Shape s: shapes) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.draw(this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 现在，规则要求drawAll方法只能用于仅包含Shape对象的List，例如它不能用于List&lt;Circle&gt;。但不幸的是，由于所有的方法所做的只是从List中读取Shape对象，所以它也需要能用于List&lt;Circle&gt;。我们所想要的就是这个方法能够接受所有的 Shape类型。<br />
&nbsp;&nbsp;&nbsp; public void drawAll(List&lt;? extends Shape&gt; shapes) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 这儿是一个很小但很重要的区别：我们已经用List&lt;? extends Shape&gt;代替了List&lt;Shape&gt;。现在，drawAll方法就可以接受Shape的任何子类对象的List了。<br />
&nbsp;&nbsp;&nbsp; List&lt;? extends Shape&gt;就是有边界的通配符的一个例子。问号(?)代表未知类型，就如我们之前所看到的这个通配符一样。然而，在这个例子中，我们这个未知类型实际上是Shape类的子类。(注：它可以是Shape类型本身；无需按字面上的意义一定说是Shape子类)。<br />
&nbsp;&nbsp;&nbsp; 一般地，在使用通配符时要付出一些弹性方面的代价。这个代价就是，马上向该方法体中写入Shape类型的对象是非法的。例如，下面的代码是不被允许的：<br />
&nbsp;&nbsp;&nbsp; public void addRectangle(List&lt;? extends Shape&gt; shapes) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shapes.add(0, new Rectangle()); // Compile-time error!<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 你应该会指出为什么上面的代码是不能被接受的。shaps.add的第二个参数是"? extends Shape"--一个未知的Shape子类，由于我们不知道它会是哪个Shape类型，不知道它的超类是否就是Rectangle；它可能是，也可能不是 Rectangle的超类，所以当传递一个Rectangle对象，并不安全。<br />
&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt;">有边界的</span><span style="font-size: 10pt;">通配符正是上一节中DMV向人口调查局提交数据的例子所需要的。我们的例子假设那些数据是由姓名(用字符串表示)到人(用Person或其子类，如 Driver，的引用类型表示)的映射表示。Map&lt;K, V&gt;是包含两个类型参数的例子，这两个类型参数分别表示映射中的键与值。<br />
&nbsp;&nbsp;&nbsp; 再次注意形式类型参数的命名规范--K代表键，V代表值。<br />
&nbsp;&nbsp;&nbsp; public class Census {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void addRegistry(Map&lt;String, ? extends Person&gt; registry) {<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; ...<br />
<br />
&nbsp;&nbsp;&nbsp; Map&lt;String, Driver&gt; allDrivers = ... ;<br />
&nbsp;&nbsp;&nbsp; Census.addRegistry(allDrivers);<br />
<br />
<span style="font-weight: bold;">5 泛型方法</span><br />
&nbsp;&nbsp;&nbsp; 考虑写一个方法，它包含一个Object数据和一个集合对象，它的作用是将数组中的对象全部插入到集合对象中。下面是第一次尝试：<br />
&nbsp;&nbsp;&nbsp; static void fromArrayToCollection(Object[] a, Collection&lt;?&gt; c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Object o : a) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(o); // Compile time error<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 到现在为此，你要学会避免新手所犯的错误--尝试将Collection&lt;Object&gt;作为这个集合的类型参数。你可能认识或没认识到使用 Collection&lt;?&gt;也不能完成工作。回忆一下，你不能将对象挤入一个未知类型的集合对象中。<br />
&nbsp;&nbsp;&nbsp; 处理这些问题的方法是使用泛型方法。就像类型的声明一样，方法的声明也可以泛型化--即，用一个或多个参数去参数化这个方法。<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; void fromArrayToCollection(T[] a, Collection&lt;T&gt; c) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (T o : a) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(o); // Correct<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 我们能够调用任意类型的集合对象中的方法，只要这个集合对象中的元素是数组类型中元素的超类型。<br />
&nbsp;&nbsp;&nbsp; Object[] oa = new Object[100];<br />
&nbsp;&nbsp;&nbsp; Collection&lt;Object&gt; co = new ArrayList&lt;Object&gt;();<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(oa, co); // T inferred to be Object<br />
<br />
&nbsp;&nbsp;&nbsp; String[] sa = new String[100];<br />
&nbsp;&nbsp;&nbsp; Collection&lt;String&gt; cs = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(sa, cs); // T inferred to be String<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(sa, co); // T inferred to be Object<br />
<br />
&nbsp;&nbsp;&nbsp; Integer[] ia = new Integer[100];<br />
&nbsp;&nbsp;&nbsp; Float[] fa = new Float[100];<br />
&nbsp;&nbsp;&nbsp; Number[] na = new Number[100];<br />
&nbsp;&nbsp;&nbsp; Collection&lt;Number&gt; cn = new ArrayList&lt;Number&gt;();<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(ia, cn); // T inferred to be Number<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(fa, cn); // T inferred to be Number<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(na, cn); // T inferred to be Number<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(na, co); // T inferred to be Object<br />
<br />
&nbsp;&nbsp;&nbsp; fromArrayToCollection(na, cs); // compile-time error<br />
&nbsp;&nbsp;&nbsp; 注意我们并不需要传递一个确切的类型给泛型方法。编译器会根据准确的参数的类型帮我们推断出实际类型参数。编译器通常会推断出大部分的特定类型参数，这就使得对方法的调用是类型正确的。<br />
&nbsp;&nbsp;&nbsp; 产生了一个问题：什么时候我应该使用泛型方法，什么时候我应用使用通配符类型？为了理解答案，让我们测试一些集合框架类库中的方法：<br />
&nbsp;&nbsp;&nbsp; interface Collection&lt;E&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean containsAll(Collection&lt;?&gt; c);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean addAll(Collection&lt;? extends E&gt; c);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 我们可能使用下面的泛型方法替换上面的程序：<br />
&nbsp;&nbsp;&nbsp; interface Collection&lt;E&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public &lt;T&gt; boolean containsAll(Collection&lt;T&gt; c);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public &lt;T extends E&gt; boolean addAll(Collection&lt;T&gt; c);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Hey, type variables can have bounds too!<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 然而，在两个containAll和addAll方法中，类型参数T只被使用了一次。返回类型既不依赖类型参数，也不需要传递其它的参数给这个方法(在本例中，只不过是一个实参罢了)。这就告诉我们该实参将用于多态；它的仅有的作用就是允许该方法的多种不同的实参能够应用于不同的调用点。<br />
&nbsp;&nbsp;&nbsp; 泛型方法允许类型参数用于描述一个或多个实参的类型对于该方法和/或它的返回值之间依赖关系。如果没有这种依赖关系，那么就不应该使用泛型方法。<br />
&nbsp;&nbsp;&nbsp; 一前一后的使用泛型方法和通配符是可能的，下面的方法Collections.copy()就表现了这一点：&nbsp;&nbsp;&nbsp; class Collections {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static &lt;T&gt; void copy(List&lt;T&gt; dest, List&lt;? extends T&gt; src) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 注意这两个参数的类型之间的依赖关系。任何复制于源表scr的对象对于目标表dest中元素的类型T都必须是可赋值的。所以src元素的类型肯定是T的任何子类型--我们不用关心这些。复制方法的签名使用一个类型参数描述了这种依赖关系，但将通配符用于第二个参数中元素的类型。<br />
&nbsp;&nbsp;&nbsp; 我们也可以使用另一种方法来书写这个方法的签名，这种方法完全不需要使用通配符：<br />
&nbsp;&nbsp;&nbsp; class Collections {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static &lt;T, S extends T&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void copy(List&lt;T&gt; dest, List&lt;S&gt; src) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 这很好，但当第一个类型参数在类型dest和第二个类型的限度中都使用了时，S它那本身只被使用了一次，就是在src的类型中--没任何其它的东西再依赖于它了。这就是一个我们要以使用通配符替换S的一个信号。使用通配符比显示的声明类型变量更加清晰、更加精确，所以在任何可能的时候通配符是首选。<br />
&nbsp;&nbsp;&nbsp; 通配符也有它的优点，它可以被用于方法签名的外面，以作为字段的类型，局部变量或数组。下面就是这样的一个例子。<br />
&nbsp;&nbsp;&nbsp; 回到我们绘制形状的那个例子，假设我们想维护一个绘制形状请求的历史记录。我们可以将这个历史记录维护在类Shape内部的一个静态变量，让drawAll方法将它自己获得的实参(即要求绘制的形状)加入历史字段中。<br />
&nbsp;&nbsp;&nbsp; static List&lt;List&lt;? extends Shape&gt;&gt; history =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ArrayList&lt;List&lt;? extends Shape&gt;&gt;();<br />
&nbsp;&nbsp;&nbsp; public void drawAll(List&lt;? extends Shape&gt; shapes) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; history.addLast(shapes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Shape s: shapes) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.draw(this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 最后，仍然让我们再次注意类型变量的命名规范。我们一般使用T表示类型，只要无需再区别任何其它的特定类型。这种情况经常用于泛型方法中。如果有多个类型参数，我可以使字母表中邻近T的其它字母，例如S。如果在一个泛型类中有一个泛型方法，那么为了避免混淆，一个好的习惯是不要使泛型类和泛型方法有相同名字的类型参数。这也适用于嵌套泛型类。<br />
<br />
<span style="font-weight: bold;">6 与遗留代码交互</span><br />
&nbsp;&nbsp;&nbsp; 到现在为止，我们的例子是假设处于一种理想的状况，即每个人都在使用Java程序设计语言的支持泛型的最新版。<br />
&nbsp;&nbsp;&nbsp; 唉，但现实并非如此。数以百万行计的代码是用Java语言的早期版本写的，而且也不可能在一夜之间就将它们转换到新版中。<br />
&nbsp;&nbsp;&nbsp; 稍后，在"使用泛型转化遗留代码"这一节中，我们将解决将你的旧代码转换到使用泛型这个问题。在本节，我们将关注一个简单的问题：遗留代码与泛型代码之间如何交互？这个问题含有两个部分：在泛型代码内部使用遗留代码；在遗留代码内部使用泛型代码。<br />
&nbsp;&nbsp;&nbsp; 作为一个例子，假设你想使用包com.Fooblibar.widgets。分支Fooblibar.com*商用在一个资产管理系统中，这个系统的精华如下所示：<br />
&nbsp;&nbsp;&nbsp; package com.Fooblibar.widgets;<br />
<br />
&nbsp;&nbsp;&nbsp; public interface Part { ...}<br />
<br />
&nbsp;&nbsp;&nbsp; public class Inventory {<br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * Adds a new Assembly to the inventory database.<br />
&nbsp;&nbsp;&nbsp;&nbsp; * The assembly is given the name name, and consists of a set<br />
&nbsp;&nbsp;&nbsp;&nbsp; * parts specified by parts. All elements of the collection parts<br />
&nbsp;&nbsp;&nbsp;&nbsp; * must support the Part interface.<br />
&nbsp;&nbsp;&nbsp; **/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void addAssembly(String name, Collection parts) {...}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static Assembly getAssembly(String name) {...}<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public interface Assembly {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection getParts(); // Returns a collection of Parts<br />
&nbsp;&nbsp;&nbsp; }<br />
现在，你要添加一些新的代码并使用上述API。比较好的是，要确保你一直能够使用适当的实参去调用addAssembly方法--即，你传入的集合对象必须是装有Part对象的集合对象。当然，泛型最适合做这些了：<br />
&nbsp;&nbsp;&nbsp; package com.mycompany.inventory;<br />
<br />
&nbsp;&nbsp;&nbsp; import com.Fooblibar.widgets.*;<br />
<br />
&nbsp;&nbsp;&nbsp; public class Blade implements Part {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Guillotine implements Part {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Main {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;Part&gt; c = new ArrayList&lt;Part&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(new Guillotine()) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(new Blade());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Inventory.addAssembly("thingee", c);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;Part&gt; k = Inventory.getAssembly("thingee").getParts();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
当我们调用addAssembly方法时，该方法希望第二个参数的类型是Collection。该参数的实际类型是Collection&lt; Part&gt;。这是正确的，但是什么呢？毕竟，大部分的Collection是不能包含Part对象的，因为一般来说，编译器无法知道该 Collection所表示的是哪种对象的集合对象。<br />
&nbsp;&nbsp;&nbsp; 在合适的泛型代码中，Collection将一直跟随着一个类型参数。当一个像Collection这样的泛型类型在被使用时没有提供类型参数，就被称之为原生类型(Raw Type)。<br />
&nbsp;&nbsp;&nbsp; 大多数人的每一直觉认为Collection就是Collection&lt;Object&gt;。然而，按我们之前所说的，在需要Collection&lt;Object&gt;的地方使用Collection&lt;Part&gt;并不是安全的。<br />
&nbsp;&nbsp;&nbsp; 但请等等，那也不对！想想对getParts对象的调用，它要返回一个Collection对象(实际上是一个引用变量)。然后这个对象被赋于变量k，k是 Collection&lt;Part&gt;类型。如果调用该方法而返回的结果是一个Collection&lt;?&gt;对象，该赋值操作也将产生错误。<br />
&nbsp;&nbsp;&nbsp; 事实上，该赋值操作是合法的，它会生产一个未检查的警告。这个警告是必要的，因为事实上编译器并不能保证它的正确性。我们没办法检查 getAssembly方法中的遗留代码以保证返回的集合对象Part对象的集合。被用于该代码的类型是Collection，能够合法的向这种 Collection中插入任何类型的对象。<br />
&nbsp;&nbsp;&nbsp; 那么这还应该是一个错误吗？就理论上而言，是的；但就实际上而言，如果泛型代码是为了调用遗留代码，那么就不得不允许了。对于你，一个程序员，会对这种情况感到满意的，赋值是安全的，因为getAssermbly方法的规则告诉我们它返回返回的是 Part对象的Collection，即使该方法的签名并没有表明这一点。<br />
&nbsp;&nbsp;&nbsp; 所以原生类型非常像通配符类型，但它们不会被做严格的类型检查。这是经过深思熟虑之后的结果，是为了允许泛型代码能够与之前已存在的代码交互使用。<br />
&nbsp;&nbsp;&nbsp; 用泛型代码调用遗留代码是天生危险的；一旦你在泛型代码中混合了非泛型的遗留代码，那么泛型类型系统通常都无法提供完全的保证。然而，这仍然比你不使用泛型要好些。至少你知道最终这些代码是一致的。<br />
&nbsp;&nbsp;&nbsp; 碰到那儿已经有了很多的非泛型代码，然后又有了泛型代码的时候，那么无法避免的情况就是不得不混合它们。<br />
&nbsp;&nbsp;&nbsp; 如果你发现你必须混合使用遗留代码和泛型代码，请密切注意未检查的警告。要谨慎地思考你如何再才能证明那些被给出了危险警告的代码是安全的。<br />
&nbsp;&nbsp;&nbsp; 当你继续犯错误，且代码造成的警告确实不是类型安全的，什么事情将发生呢？让我们看看这样的一种情况。在这个处理过程中，我们将观察编译器所做的事情。<br />
<br />
<span style="font-weight: bold;">擦除和翻译</span><br />
&nbsp;&nbsp;&nbsp; public String loophole(Integer x) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List&lt;String&gt; ys = new LinkedList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List xs = ys;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xs.add(x); // Compile-time unchecked warning<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ys.iterator().next();<br />
&nbsp;&nbsp;&nbsp; }<br />
此处，我们已经别名化了String的List和一个普通的老版的List。我们向这个List xs插入一个Integer对象，并试图抽取一个String对象。这显然是错的。如果我们忽略警告并尝试执行这段代码，它将在我们试图使用错误类型的地方上失败。<br />
&nbsp;&nbsp;&nbsp; public String loophole(Integer x) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List ys = new LinkedList;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List xs = ys;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xs.add(x);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return(String) ys.iterator().next(); // run time error<br />
&nbsp;&nbsp;&nbsp; }<br />
当我们从这个List中抽取一个元素，并试图将它当作String对象而把它转换成String时，我们将得到一个ClassCastException的异常。完全相同的情况也发生在了loophole方法的泛型版中。<br />
&nbsp;&nbsp;&nbsp; 这种情况的原因就是泛型是由Java编译器作为一种叫做"擦除(Erasure)"的最前到后的机制实现的。你(几乎)可以把它想像为一种"源代码对源代码"(source-to-source)的翻译，这就是为何loophole的泛型版被转换成了非泛型版了。<br />
&nbsp;&nbsp;&nbsp; 结果，Java虚拟机的类型安全和完整性再也不处于危险中了，甚至在遇到到未检查的警告时也一样。<br />
&nbsp;&nbsp;&nbsp; 基本地，Erasure去除(或者说"擦除")了所有的泛型信息。所有的在角括号中的类型信息都被抛弃了，所以，如像List&lt;String&gt; 这样的参数化类型被转化成了List。所有保持对类型变量使用的地方都被类型变量的高层限度类型(一般就是Object)替换了。并且，无论何时产生的结果都不是类型正确的，一个向适当的类型的强制类型转换被插入了其中。<br />
&nbsp;&nbsp;&nbsp; 对Erasure的全部细节的描述超出了本教程的范畴，但我们给出的简单描述离真实情况并不太远。了解一些这方面的知识是有益的，特别是如果你想做一些更加老练的泛型应用，如把已有的API转换到使用泛型时(详见"使用泛型转化遗留代码")，或者只是想理解为什么它们会是这种情况。<br />
<br />
<span style="font-weight: bold;">&nbsp;&nbsp;&nbsp; 在遗留代码中使用泛型代码</span><br />
&nbsp;&nbsp;&nbsp; 现在让我们思考一个颠倒的例子。想像Foolibar.com选择泛型去转化了它们的API，但他们的一些客户端程序还没有转化。所以这些代码看起来像：<br />
&nbsp;&nbsp;&nbsp; package com.Fooblibar.widgets;<br />
<br />
&nbsp;&nbsp;&nbsp; public interface Part {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Inventory {<br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * Adds a new Assembly to the inventory database.<br />
&nbsp;&nbsp;&nbsp;&nbsp; * The assembly is given the name name, and consists of a set<br />
&nbsp;&nbsp;&nbsp;&nbsp; * parts specified by parts. All elements of the collection parts<br />
&nbsp;&nbsp;&nbsp;&nbsp; * must support the Part interface.<br />
&nbsp;&nbsp;&nbsp; **/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void addAssembly(String name, Collection&lt;Part&gt; parts) {...}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static Assembly getAssembly(String name) {...}<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public interface Assembly {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;Part&gt; getParts(); // Returns a collection of Parts<br />
&nbsp;&nbsp;&nbsp; }<br />
客户端程序看起来像：<br />
&nbsp;&nbsp;&nbsp; package com.mycompany.inventory;<br />
<br />
&nbsp;&nbsp;&nbsp; import com.Fooblibar.widgets.*;<br />
<br />
&nbsp;&nbsp;&nbsp; public class Blade implements Part {<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Guillotine implements Part {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Main {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection c = new ArrayList();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(new Guillotine()) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.add(new Blade());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Inventory.addAssembly("thingee", c); // 1: unchecked warning}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection k = Inventory.getAssembly("thingee").getParts();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 这些客户端代码是在泛型产生之前写成的，但它使用了包com.Fooblibar.widgets和集合框架类库，这两者都在使用泛型。客户端中对泛型类型的使用使得它们成为了原生(Raw Type)类型。<br />
&nbsp;&nbsp;&nbsp; 代码行1产生了一个未检查的警告，因为一个原生Collection被传入了一个期望是Collection&lt;Part&gt;出现的地方，而且编译器无法保证这个原生Collection真的就是Part对象的Collection。<br />
&nbsp;&nbsp;&nbsp; 作为一种可选的方法，你可以将这些代码作为Java 1.4的源代码进行编译，这就能保证不会出现警告。但这样的话，你将不能使用到JDK 5.0中任何新的语言特性。<br />
&nbsp;&nbsp;&nbsp; --------------------------------------------------------------------------<br />
&nbsp;&nbsp;&nbsp; 注意，"Fooblibar.com"是一个纯属虚构的公司，目的仅仅只是为了本文中的例子。任何公司或机构、任何健在或已故的个人与此有关的话，纯属巧合。<br />
&nbsp;&nbsp;&nbsp; 译者：看来老外做事情十分谨慎，对于这种"小问题"我们又怎么会如此郑重其事的发表一个声明呢。<br />
<br />
<span style="font-weight: bold;">7 良好的打印</span><br />
&nbsp;&nbsp;&nbsp; 一个泛型类被它的所有应用共享<br />
&nbsp;&nbsp;&nbsp; 下面的代码片断是打印出什么呢？<br />
&nbsp;&nbsp;&nbsp; List &lt;String&gt; l1 = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp; List&lt;Integer&gt; l2 = new ArrayList&lt;Integer&gt;();<br />
&nbsp;&nbsp;&nbsp; System.out.println(l1.getClass() == l2.getClass());<br />
&nbsp;&nbsp;&nbsp; 你可能会被引诱得说是false，但你错了。打印的是true，因为一个泛型类的所有实际拥有相同的运行时类，而不管它们具体的类型参数。<br />
&nbsp;&nbsp;&nbsp; 确实，对一个类的泛型所做的事实就是这个泛型类对它所有可能的类型参数都有相同的行为；相同的这个类可以被视为它有很多不同的类型。<br />
&nbsp;&nbsp;&nbsp; 同样的结果，泛型类中的静态变量和方法也被该类的所有实例共享。这就是为什么在一个静态方法或初始化器中、在一个静态变量的声明或初始化器中引用类型变量是非法的。<br />
<br />
&nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">Cast和Instanceof</span><br />
&nbsp;&nbsp;&nbsp; 一个泛型类被它的所有实例共享的另一个隐含意义就是，如果某个实例是这个泛型类的一种特定类型的实例，那么通常情况下请求这个类的实例是无意义的：<br />
&nbsp;&nbsp;&nbsp; Collection cs = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp; if (cs instanceof Collection&lt;String&gt;) { ...} // Illegal.<br />
&nbsp;&nbsp;&nbsp; 类似地，如下面这个强制类型转换<br />
&nbsp;&nbsp;&nbsp; Collection&lt;String&gt; cstr = (Collection&lt;String&gt;) cs; // Unchecked warning,<br />
&nbsp;&nbsp;&nbsp; 会报一个未检查的警告，因为这不应该是运行时系统将要为你检查的事情。<br />
&nbsp;&nbsp;&nbsp; 对类型变量也是如此<br />
&nbsp;&nbsp;&nbsp; &lt;T&gt; T badCast(T t, Object o) {return (T) o; // Unchecked warning.<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 类型变量在运行时并不存在。这就意味着在时间和空间上，它们都不可能避免地无法产生作用。不幸的是，这也意味着你不能可靠地在强制类型转换中使用它们。<br />
<br />
&nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">数组</span><br />
&nbsp;&nbsp;&nbsp; 一个数组对象中元素的类型不会是一个类型变量或参数化的类型，除非它是一个(非受限的)通配符类型。你可以声明数组类型的元素类型是一个类型变量或参数化的类型，但数组对象本身不行。<br />
&nbsp;&nbsp;&nbsp; 这很烦人，但却是真的。该约束对避免如下例子中的情况是有必要的：<br />
&nbsp;&nbsp;&nbsp; List&lt;String&gt;[] lsa = new List&lt;String&gt;[10]; // Not really allowed.<br />
&nbsp;&nbsp;&nbsp; Object o = lsa;<br />
&nbsp;&nbsp;&nbsp; Object[] oa = (Object[]) o;<br />
&nbsp;&nbsp;&nbsp; List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br />
&nbsp;&nbsp;&nbsp; li.add(new Integer(3));<br />
&nbsp;&nbsp;&nbsp; oa[1] = li; // Unsound, but passes run time store check<br />
&nbsp;&nbsp;&nbsp; String s = lsa[1].get(0); // Run-time error: ClassCastException.<br />
如果允许有参数化类型的数组，上面的例子将会通过编译且不报任何未检查的警告，然而会在运行时失败。我们已经知道设计泛型的主要目的就是为了类型安全。特别地说，Java语言被设计为，如果你的整个程序使用javac -source 1.5进行编译时没有报任何未检查的警告，那么这个程序就是类型安全的。<br />
&nbsp;&nbsp;&nbsp; 然而，你仍然可以使用通配符数组。这儿有上面代码的两个变种。第一个变种放弃使用参数化类型的数组对象和参数化类型元素。这样我们为了在数组外得到String对象不得不在显示地使用强制类型转换。<br />
&nbsp;&nbsp;&nbsp; List&lt;?&gt;[] lsa = new List&lt;?&gt;[10]; // OK, array of unbounded wildcard type.<br />
&nbsp;&nbsp;&nbsp; Object o = lsa;<br />
&nbsp;&nbsp;&nbsp; Object[] oa = (Object[]) o;<br />
&nbsp;&nbsp;&nbsp; List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br />
&nbsp;&nbsp;&nbsp; li.add(new Integer(3));<br />
&nbsp;&nbsp;&nbsp; oa[1] = li; // Correct.<br />
&nbsp;&nbsp;&nbsp; String s = (String) lsa[1].get(0); // Run time error, but cast is explicit.<br />
在第二个变种中，我们限制了数组对象的创建，这个数组的元素的类型被参数化了，但仍然要将一个参数化的元素类型用于这个数组。这是合法的，但产生一个未检查的警告。确实，这段代码是不安全的，甚至会导致一个错误。<br />
&nbsp;&nbsp;&nbsp; List&lt;String&gt;[] lsa = new List&lt;?&gt;[10]; // Unchecked warning. This is unsafe!<br />
&nbsp;&nbsp;&nbsp; Object o = lsa;<br />
&nbsp;&nbsp;&nbsp; Object[] oa = (Object[]) o;<br />
&nbsp;&nbsp;&nbsp; List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br />
&nbsp;&nbsp;&nbsp; li.add(new Integer(3));<br />
&nbsp;&nbsp;&nbsp; oa[1] = li; // Correct.<br />
&nbsp;&nbsp;&nbsp; String s = lsa[1].get(0); // Run time error, but we were warned.<br />
译者：根据我的测试(JDK 1.5.0_11)，"List&lt;String&gt;[] lsa = new List&lt;?&gt;[10]"这一句无法通过编译，理由也很直观"类型不匹配，不能将List&lt;?&gt;[]转化为List&lt; String&gt;[]"。<br />
&nbsp;&nbsp;&nbsp; 类似地，试图创建一个元素类型是类型变量的数组对象会导致一个运行时错误：<br />
&nbsp;&nbsp;&nbsp; &lt;T&gt; T[] makeArray(T t) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new T[100]; // Error.<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; 因为类型变量在运行时并不存在，这就没有办法确定数组的实际类型。<br />
&nbsp;&nbsp;&nbsp; 围绕着这些限制的工作方法是使用了将类字面量当作运行时类型标记的机制，该机制将在下一节"类字面量作为运行时标记"中进行叙述。<br />
<br />
<span style="font-weight: bold;">8 类字面量作为运行时标记</span><br />
&nbsp;&nbsp;&nbsp; JDK 5.0的变量之一就是java.lang.Class也被泛型化了。这是一个不容器类而在其它地方使用泛型机制的有趣例子。<br />
&nbsp;&nbsp;&nbsp; 既然Class类有一个类型参数T，你可能会问，这个T代表什么？它代表这个Class对象表示的类型。<br />
&nbsp;&nbsp;&nbsp; 例如，String.class的类型是Class&lt;String&gt;，而Serializable.class的类型就是Class&lt;Serializable&gt;。这种机制用于提高在你的反射程序中的类型安全性。<br />
&nbsp;&nbsp;&nbsp; 特别地，由于Class类中的方法netInstance现在是返回一个T，这样当你在使用反射机制创建对象时能够得到更加精确的类型。<br />
&nbsp;&nbsp;&nbsp; 例如，假设你需要一个执行数据库查询的工具方法，给入的是SQL字符串，返回的是数据库中匹配该查询语言的对象的集合。<br />
&nbsp;&nbsp;&nbsp; 一种方法就是显示地传入一个工厂对象中，所写的代码就像：<br />
&nbsp;&nbsp;&nbsp; interface Factory&lt;T&gt; { T make();}<br />
<br />
&nbsp;&nbsp;&nbsp; public &lt;T&gt; Collection&lt;T&gt; select(Factory&lt;T&gt; factory, String statement) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;T&gt; result = new ArrayList&lt;T&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Run sql query using jdbc */ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (/* Iterate over jdbc results. */) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T item = factory.make();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Use reflection and set all of item's fields from sql results. */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.add(item);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return result;<br />
&nbsp;&nbsp;&nbsp; }<br />
你可以像下面那么样去调用<br />
&nbsp;&nbsp;&nbsp; select(new Factory&lt;EmpInfo&gt;(){ public EmpInfo make() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new EmpInfo();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , "selection string");<br />
你也可以声明一个EmpInfoFactory类去支持Factory接口<br />
&nbsp;&nbsp;&nbsp; class EmpInfoFactory implements Factory&lt;EmpInfo&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public EmpInfo make() { return new EmpInfo();}<br />
&nbsp;&nbsp;&nbsp; }<br />
然后像下面那样去调用它<br />
&nbsp;&nbsp;&nbsp; select(getMyEmpInfoFactory(), "selection string");<br />
这个解决方案最终还需要：<br />
&nbsp;&nbsp;&nbsp; * 在调用点使用冗长的匿名工厂类，<br />
&nbsp;&nbsp;&nbsp; * 或者，为每个被使用的类型声明一个工厂类，并将这个工厂类的实例传递到调用点，但这种方法有点不自然。<br />
可以很自然地将类字面量用作工厂对象，这个工厂稍后可被反射机制使用。现在这个程序(不用泛型)可以写为：<br />
&nbsp;&nbsp;&nbsp; Collection emps = sqlUtility.select(EmpInfo.class, "select * from emps");<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; public static Collection select(Class c, String sqlStatement) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection result = new ArrayList();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Run sql query using jdbc. */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (/* Iterate over jdbc results. */ ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object item = c.newInstance();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Use reflection and set all of item's fields from sql results. */ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.add(item);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return result;<br />
&nbsp;&nbsp;&nbsp; }<br />
可是，这不能给我们一个所期望的精确类型的集合。既然Class是泛型的，我们可以使用下面的代替写法：<br />
&nbsp;&nbsp;&nbsp; Collection&lt;EmpInfo&gt; emps =<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; sqlUtility.select(EmpInfo.class, "select * from emps");<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; public static &lt;T&gt; Collection&lt;T&gt; select(Class&lt;T&gt; c, String sqlStatement) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;T&gt; result = new ArrayList&lt;T&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Run sql query using jdbc. */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (/* Iterate over jdbc results. */ ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T item = c.newInstance();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Use reflection and set all of item's fields from sql results. */ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.add(item);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return result;<br />
&nbsp;&nbsp;&nbsp; }<br />
上面的程序以一种类型安全的方法给了我们精确类型的集合。<br />
&nbsp;&nbsp;&nbsp; 将类字面量作为运行时标记的技术被认为十分狡猾。例如，为了操作Annotation，这种技术在新API中被扩展使用了。<br />
<br />
<span style="font-weight: bold;">9 通配符的更多趣味</span><br />
&nbsp;&nbsp;&nbsp; 在本节，我们将考虑一些更高级的通配符用法。我们已经看了几个受限的通配符用于读取数据结构时例子。现在反过来想想一个只可写的数据结构。接口Sink是这种类型的一个简单的例子：<br />
&nbsp;&nbsp;&nbsp; interface Sink&lt;T&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flush(T t);<br />
&nbsp;&nbsp;&nbsp; }<br />
我们可以想像将它作为一个范例用于下面的代码。方法writeAll被设计为刷新集合coll中的所有元素到Sink的实例snk中，并返回最后一个被刷新的元素。<br />
&nbsp;&nbsp;&nbsp; public static &lt;T&gt; T writeAll(Collection&lt;T&gt; coll, Sink&lt;T&gt; snk) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T last;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (T t : coll) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last = t;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snk.flush(last);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return last;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; Sink&lt;Object&gt; s;<br />
&nbsp;&nbsp;&nbsp; Collection&lt;String&gt; cs;<br />
&nbsp;&nbsp;&nbsp; String str = writeAll(cs, s); // Illegal call.<br />
就已经写出来的，对writeAll方法的调用是非法的，由于无法推断出有效的类型实参；String或Object都不是T的合适类型，因为Collection的元素和Sink必须是相同的类型。<br />
&nbsp;&nbsp;&nbsp; 我们可以通过修改writeAll的方法签名来修正这个错误，如下所示，使用了通配符：<br />
&nbsp;&nbsp;&nbsp; public static &lt;T&gt; T writeAll(Collection&lt;? extends T&gt;, Sink&lt;T&gt;) {...}<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; String str = writeAll(cs, s); // Call is OK, but wrong return type.<br />
该调用是合法的，但赋值是错的，是由于返回类型被推断成了Object，因为T匹配s的类型，但s的类型是Object。<br />
&nbsp;&nbsp;&nbsp; 该解决方案使用了一种我们尚未见过的受限通配符形式：有一个较低限度的通配符。语法"? super T"表示未知类型是T的超类型(或者是T本身；记住，超类型关系是弹性的)。<br />
&nbsp;&nbsp;&nbsp; public static &lt;T&gt; T writeAll(Collection&lt;T&gt; coll, Sink&lt;? super T&gt; snk) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; String str = writeAll(cs, s); // Yes!<br />
使用了这种语法，方法的调用就是合法的，并且被推断的类型正如所愿是String。<br />
&nbsp;&nbsp;&nbsp; 现在让我们转向更为实际的例子。java.util.TreeSet&lt;E&gt;表示了一个排序了的以类型为E的对象作为元素的树。构造一个 TreeSet对象的方法之一是传递一个Comparator对象给这个构造器。该Comparator对象将被用于根据期望的规则对TreeSet中的元素进行排序。<br />
&nbsp;&nbsp;&nbsp; TreeSet(Comparator&lt;E&gt; c)<br />
&nbsp;&nbsp;&nbsp; Comparator接口是必须的：<br />
&nbsp;&nbsp;&nbsp; interface Comparator&lt;T&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int compare(T fst, T snd);<br />
&nbsp;&nbsp;&nbsp; }<br />
假设我们想创建一个TreeSet&lt;String&gt;对象，并传入一个合适的比较器对象。我们就需要一个能比较String的 Comparator对象，一个Comparator&lt;String&gt;就可以做到，但一个Comparator&lt;Object&gt; 对象也能做到。然而，我们不能调用上面Comparator&lt;Object&gt;所提供的构造器。<br />
&nbsp;&nbsp;&nbsp; TreeSet(Comparator&lt;? super E&gt; c)<br />
上述代码允许适用的比较器被使用。<br />
&nbsp;&nbsp;&nbsp; 作为最后一个低位受限通配符的例子，让我们看看Collections.max方法，该方法返回一个集合中的极大元素。为了让max文件能够工作，集合中所有的传入该集合的元素都必须实现了Comparable接口。此外，它们相互之间必须是可被比较的。<br />
&nbsp;&nbsp;&nbsp; 在第一次尝试创建这个方法后有如下结果：<br />
&nbsp;&nbsp;&nbsp; public static &lt;T extends Comparable&lt;T&gt;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T max(Collection&lt;T&gt; coll)<br />
即，这个方法有一个某类型T的集合对象，T的实例之间可以进行比较，该方法并返回一个该类型的元素。然而，这个程序实现起来太受限制了。看看是为什么，考虑一个对象，它能与任意对象进行比较：<br />
&nbsp;&nbsp;&nbsp; class Foo implements Comparable&lt;Object&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; Collection&lt;Foo&gt; cf = ... ;<br />
&nbsp;&nbsp;&nbsp; Collections.max(cf); // Should work.<br />
Collection cf中的每个元素都能与该集合中的其它元素进行比较，因为每个这样的元素都是一个Foo的实例，而Foo的实例能够与任意对象进行比较，则与另一个Foo 对象比较那就更没问题了。然而，使用前面的方法签名，我们可以发现上面对方法max的调用会被拒绝。被推断出的类型必须是Foo，但Foo并没有实现 Comparable&lt;Foo&gt;。<br />
&nbsp;&nbsp;&nbsp; 没有必要精确地要求T与它自己的实例进行比较。所有被要求的是T的实例能够与它的某个超类型的实例进行比较。这就让我们有了如下代码：<br />
&nbsp;&nbsp;&nbsp; public static &lt;T extends Comparable&lt;? super T&gt;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T max(Collection&lt;T&gt; coll)<br />
注意到Collections.max真实的方法签名更难以理解。我们将在下一节"将遗留代码转化到使用泛型"中再讲述它。这个适用于几乎任何一个 Comprarable应用的理论是打算能用于任意的类型：你总是想使用Comprarable&lt;? super T&gt;。<br />
&nbsp;&nbsp;&nbsp; 一般地，如果你的API只是将类型参数T作为类型变量使用，那就应该利于低位受限通配符(? super T)。相反地，如果这个API只需返回T，你就要使用高位受限通配符(? extends T)以给这个API的客户端程序更大的灵活性。<br />
<br />
通配符捕获<br />
&nbsp;&nbsp;&nbsp; 到目前为此，下面的程序应该更清晰些：<br />
&nbsp;&nbsp;&nbsp; Set&lt;?&gt; unknownSet = new HashSet&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; /** Add an element&nbsp; t to a Set s. */<br />
&nbsp;&nbsp;&nbsp; public static &lt;T&gt; void addToSet(Set&lt;T&gt; s, T t) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; }<br />
但下面的调用是非法的。<br />
&nbsp;&nbsp;&nbsp; addToSet(unknownSet, "abc"); // Illegal.<br />
传入该方法的一个精确的Set是一个String的Set这没有影响；问题在于作为实参传入表达式的是一个未知类型的Set，这并不能保证它一定就是String或其它任何特定类型的Set。<br />
&nbsp;&nbsp;&nbsp; 现在考虑下面的代码：<br />
&nbsp;&nbsp;&nbsp; class Collections {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;T&gt; public static Set&lt;T&gt; unmodifiableSet(Set&lt;T&gt; set) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; Set&lt;?&gt; s = Collections.unmodifiableSet(unknownSet); // This works! Why?<br />
看起来它应该不被允许；然而，看看这个特殊的调用，它确实是安全的而可以允许这么做。毕竟，unmodifiableSet方法可用于任何类型的Set，而不管这个Set中的元素的类型。<br />
&nbsp;&nbsp;&nbsp; 因为这种情况发生地相对比较频繁，所以有一个特殊的规则允许这些在一个非常特殊的环境中的代码是合法的，在这个环境中这些代码被证明是安全的。这个名为"通配符捕获"的规则允许编译器将通配符的未知类型作为类型实参推断到泛型方法中。<br />
<br style="font-weight: bold;" />
<span style="font-weight: bold;">10 将遗留代码转化为使用泛型</span><br />
&nbsp;&nbsp;&nbsp; 早先，我们展示了新、老代码之间如何交互。现在是时候看看"泛型化"老代码这个困难的问题了。<br />
&nbsp;&nbsp;&nbsp; 如果你决定将老代码转换成使用泛型，你需要仔细考虑如何去修改你的API。<br />
&nbsp;&nbsp;&nbsp; 你需要确定泛型化的API不会造成过度的限制；它必须能继续地支持API原先的功能。再次考虑一些来自于java.util.Collection中的例子。没有使用泛型的API看起来像：<br />
&nbsp;&nbsp;&nbsp; interface Collection {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean containsAll(Collection c);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean addAll(Collection c);<br />
&nbsp;&nbsp;&nbsp; }<br />
一种自然的泛型化尝试可能像下面那样：<br />
&nbsp;&nbsp;&nbsp; interface Collection&lt;E&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean containsAll(Collection&lt;E&gt; c);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean addAll(Collection&lt;E&gt; c);<br />
&nbsp;&nbsp;&nbsp; }<br />
肯定是类型安全的了，但它并没有实现该API之前的功能。containsAll方法用于任何引入的集合对象，如果引入的集合真地仅包含E的实例时，该方法才会成功。但是：<br />
&nbsp;&nbsp;&nbsp; * 引入集合的静态类型可能有所不同，或许是因为调用者不知道传入的集合对象的准确类型，或者可能是因为它是一个Collection&lt;S&gt;，而S是E的子类型。<br />
&nbsp;&nbsp;&nbsp; * 能够合法地使用一个不同的类型的集合调用containsAll方法则最为理想了。这种方法应该能工作，并将返回false。<br />
&nbsp;&nbsp;&nbsp; 在这个例子中的addAll方法，我们应该能够加入由任何由E的子类型的实例组成的集合对象。我们在"泛型方法"这一节中已经看过了如何正确地处理此类情况。<br />
&nbsp;&nbsp;&nbsp; 你也需要保证修改后的API要保持与老的客户端程序的二进制兼容性。这就暗示着"擦除"后的API必须与以前的非泛型化API相同。在大部分例子中，这自然会引用争吵，但也有一些精妙的例子。我们将测试我们已经遇到过的最精妙例子中的一个，即Collections.max()方法。根据我们在"通配符的更多乐趣"一节所看到的，一个模糊的max方法签名是：<br />
&nbsp;&nbsp;&nbsp; public static &lt;T extends Comparable&lt;? super T&gt;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T max(Collection&lt;T&gt; coll)<br />
除了擦除后的签名之外，这些都很好：<br />
&nbsp;&nbsp;&nbsp; public static Comparable max(Collection coll)<br />
这与max之前的方法签名不同：<br />
&nbsp;&nbsp;&nbsp; public static Object max(Collection coll)<br />
当然可以这样指定max方法的签名，但这没有什么用。所有老的调用Collections.max方法的二进制class文件都依赖于返回类型为Object的方法的签名。<br />
&nbsp;&nbsp;&nbsp; 通过显示地在限度中为形式类型参数T指定一个超类，我们能够强制这个擦除产生不同的结果。<br />
&nbsp;&nbsp;&nbsp; public static &lt;T extends Object &amp; Comparable&lt;? super T&gt;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T max(Collection&lt;T&gt; coll)<br />
这是一个单个类型参数有多个限度的例子，使用语法"T1 &amp; T2 ... &amp; Tn"。有多个限度的类型变量是被认为是限度中所有类型的一个子类型。当使用多限度时，限度中第一个被提及的类型将作为该类型变量被擦除后的类型。<br />
&nbsp;&nbsp;&nbsp; 最后，我们应该回想到max方法只需从输入的Collection中进行读取操作，所以这适合于T的任何子类型的集合。<br />
&nbsp;&nbsp;&nbsp; 这就把我们带入到JDK中该方法的真实签名中：<br />
&nbsp;&nbsp;&nbsp; public static &lt;T extends Object &amp; Comparable&lt;? super T&gt;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T max(Collection&lt;? extends T&gt; coll)<br />
在实践中产生如此晦涩的应用是十分罕见的，但是当转换现有API时，专家型的类库设计者们应该要准备着去进行非常细致地地思考。<br />
&nbsp;&nbsp;&nbsp; 另一个问题需要密切关注的就是"协变返回"，即在一个子类型中精炼了返回类型。你不需要在老的API中使用这个特性。为了找到原因，让我们看一个例子。<br />
&nbsp;&nbsp;&nbsp; 假设你原先的API是如下形式：<br />
&nbsp;&nbsp;&nbsp; public class Foo {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Foo create() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } // Factory. Should create an instance of whatever class it is declared in.<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Bar extends Foo {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Foo create() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } // Actually creates a Bar.<br />
&nbsp;&nbsp;&nbsp; }<br />
为了利用"协变返回"，你将它修改为：<br />
&nbsp;&nbsp;&nbsp; public class Foo {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Foo create() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } // Factory. Should create an instance of whatever class it is declared in.<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public class Bar extends Foo {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Bar create() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } // Actually creates a Bar.<br />
&nbsp;&nbsp;&nbsp; }<br />
现在假设有一个像下面那样写的你代码的第三方客户端程序：<br />
&nbsp;&nbsp;&nbsp; public class Baz extends Bar {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Foo create() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } // Actually creates a Baz.<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; Java 虚拟机不直接支持有着不同返回类型的方法的覆盖，该特性由编译器支持。因此，除非Baz类被重新编译，否则它不能正常地覆盖Bar的create方法。另外，Baz将不得不被修改，因为这些代码将如前面所写的那样被拒绝--Baz中的create方法返回类型并不是Bar中create方法返回类型的子类型。<br />
&nbsp;&nbsp;&nbsp; 译者：根据我的测试(JDK 1.5.0_11)，Baz类中的create方法无法通过编译，理由就是Baz.create方法与Bar.create方法的返回不兼容，返回类型须是Bar，而不是Foo。<br />
<br />
<span style="font-weight: bold;">致谢</span><br />
&nbsp;&nbsp;&nbsp; Erik Ernst, Christian Plesner Hansen, Jeff Norton, Mads Torgersen, Peter von der Ahe和Philip Wadler为该教程提供了材料。<br />
&nbsp;&nbsp;&nbsp; 感谢David Biesack, Bruce Chapman, David Flanagan, Neal Gafter, Orjan Petersson, Scott Seligman, Yoshiki Shibata和Kresten Krab Thorup为该教程的早期版本所提出的富有价值的反馈。向我忘记列出来的每个人道歉。</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/125293.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> 2007-06-20 11:11 <a href="http://www.blogjava.net/jiangshachina/archive/2007/06/20/125293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rock Star Josh Bloch(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/06/11/123283.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 11 Jun 2007 00:33:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/06/11/123283.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/123283.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/06/11/123283.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/123283.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/123283.html</trackback:ping><description><![CDATA[<div style="text-align: center;"><span style="font-size: 12pt; font-weight: bold;"><span style="font-size: 14pt;"><span style="font-size: 14pt;"><span style="font-size: 14pt;">Rock Star Josh Bloch</span></span></span></span><br />
</div>
<span style="font-size: 10pt;">这是一篇前不久在</span><span style="font-size: 10pt;">JavaOne 2007期间</span><span style="font-size: 10pt;">(5月初)SDN(Sun Developer Network)对<a href="http://java.sun.com/javaone/sf/2007/articles/rockstar_bloch.jsp">Joshua Bloch的访谈</a>。我看了多遍，受益良多，就翻译在了此处。由于本人英文水平有限，文中部分语句暂时无法得到准确的翻译，故英文原文一并给出，请大家斟酌。(2007.06.12最后更新)<br />
<br />
Bio: Joshua Bloch, Google's chief Java architect and a former Distinguished Engineer at Sun Microsystems, won the prestigious Jolt Award from Software Development Magazine for his book, Effective Java Programming Language Guide. At Sun, he led the design and implementation of numerous Java platform features, including JDK 5.0 language enhancements and the award-winning Java Collections Framework. He holds a Ph.D. in computer science from Carnegie-Mellon University and is most recently the co-author, with Neal Gafter, of Java Puzzlers.<br />
简历：Joshua Bloch，Google公司的首席Java架构师，也曾是Sun公司的杰出工程师。由于书籍Effective Java Programming Language Guide，他从Software Development Magazine那儿赢得了极负声名的Jolt大奖。在Sun公司时，他领导设计并实现了众多的Java平台特性，包括JDK 5语言的提升以及获奖的Java集合框架。他从卡纳基-梅隆大学获得了计算机科学博士学位。最近，他与Neal Gafter一起成为Java Puzzlers的合作作者。<br />
<br />
Q: I've heard that at your doctoral thesis defense at Carnegie-Mellon, which was open to anyone for questions, you planted a long technical question with your mother that you answered flawlessly after saying, "Awww, Mom!" Is that true?<br />
我听说你了在卡纳基-梅隆大学时谋划的博士论文，这个论文为了寻求问题而被放开给每个人。你在你母亲的帮助下弄出了一个很长的技术问题，在说出来之后，你的回答毫无瑕疵。<br />
<br />
A:Yes. The question appeared to tear the work to shreds. My mom had fun with it, and so did I. Thesis defenses can be so dry. I did everything I could to inject a bit of levity into mine. I also answered another -- planted -- question with a rap, complete with recorded rhythm track. I had a boom box concealed under the desk during the talk.<br />
是的。这个问题的出现将工作分成小块。我妈妈喜欢它，我也是的。撰写论文是如此的枯燥。我做每件事情时，都希望能够给我自己的事情加入一点乐趣。我也回答了与RAP有关的另一个问题，使用一个记录节奏跟踪器完成。在谈话的过程中，我将一个Boom Box(译注：一种便携式的音频系统，一般由无线电、磁带或CD播放器组成)藏在桌子下面。<br />
<span style="font-style: italic;">上述一问一答，我确实无法准确翻译。可能涉及到某个典故，并且语言很口语化。希望有朋友能够教我 :-D</span><br />
<br />
Q: You are currently busy revising Effective Java. Can you give us some hints about what will be in the second edition?<br />
目前，你正在修订"Effective Java"。你能否给我们一些关于可能出现在第二版中的内容的一些线索？<br />
<br />
A: I'm trying very hard to preserve the tone of the first edition. I'm revising all the existing items in light of the J2SE 5.0 language changes and everything I've learned since 2001. I'm adding a few more items here and there, plus an entire chapter on generics. Also, I'm slanting the threads chapter toward java.util.concurrent.<br />
我正在努力保持第一版的风格。我正修改的所有已经存在的条目都据于J2SE 5.0语言的变化，以及从2001年至今我所学习到的任何东西。我正在各处添加一些更多的条目，为泛型加入了一整章。为了java.util.concurrent包，我也会对多线程这一章进行倾斜。<br />
<br />
Q: What are the most interesting or funny reactions you have gotten from readers of Java Puzzlers?<br />
你已经从Java Puzzlers的读者那儿得到的最有趣或最奇特的反应是什么？<br />
<br />
A: Well, I got a letter from David Bacon thanking me for the copy I sent him and saying that it was the perfect thickness to raise his monitor up to the correct height. Then he sent me a picture of his intern's monitor with a copy of the book under it. I also thought it was pretty funny when our Japanese translator, Yoshiki Shibata, caught us falling into one of our own traps. It's described here, if you're curious.<br />
我得到了David Bacon的一封信，感谢我送给了他一本该书，并且说那是一个使他的显示器上升达到正确高度的理想厚度。后来他递给我一张他的内部显示器图片，这本书就在显示器的下方。我同样认为我们的日文版译者，Yoshiki Shibata，为赶上我们而落入了我们自己的一件行李中，是一件十分有趣的事情。就描述到这，如果你感兴趣的话。<br />
<span style="font-style: italic;">如果这两件"趣闻"真的如我翻译的那样的话，我可真感到惊讶。关于第一件趣事，如果我是该书的作者，我会认为</span></span><span style="font-size: 10pt;"><span style="font-style: italic;">David</span></span><span style="font-size: 10pt;"><span style="font-style: italic;">的行为是对我的一种极大的讽刺--我的书是用来看的，不是用来垫显示器的；关于第二件趣事，我认为就是Josh在"讽刺"(至少是无恶意的吧)那位可怜的日文译者。不过，老外很开放的，也许这只是一些平常的"笑料"罢了。</span><br />
<br />
Q: In June 2004, when you became chief Java architect at Google, you were put in charge of spreading and encouraging the use of Java technology there. How has it gone?<br />
在2004年6月，当你成为了Google公司的首席Java架构师，你在那儿就被置于推广和鼓励Java技术应用的位置。后来如何了？<br />
<br />
A: Very well indeed. We use the Java platform extensively in many of our products and internal applications. Of course, we use other languages too.<br />
真的非常好。我们在我们的产品和内部应用中广泛地使用Java平台。当然，我们也使用其它的语言。<br />
<br />
Q: Some people may not know that you identified a widespread bug affecting both Java and other languages that had gone unnoticed for half a century. Tell us about it.<br />
有些人可能不知道你发起过一个分布广泛地Bug影响，包括Java和其它语言，它已经被忽略达半个世纪之久了。告诉我们一些关于它的事情。<br />
<br />
A: It wasn't really half a century. A quarter century maybe. And I didn't really identify it -- I propagated it into the Java platform libraries! Someone reported the bug to Sun. I don't know who it was, but I was shocked -- and amused -- by the bug. At Peter Norvig's urging, I wrote it up for the Google research blog. I guess it caused quite a stir in the blogosphere.<br />
真的不是半个世纪，可能是25年。并且也确实不是我发起它的--我只是将它介绍到了Java平台库中。某个人向Sun报告了这个Bug。我不知道他是谁，但我被这个Bug震惊了--也觉得很有趣。受Peter Norvig的催促，我把它写入了Google的研究Blog。我猜想它在Blog界引起了一阵震动。<br />
<br />
Q: How do you feel about the open sourcing of the Java platform?<br />
你怎么看Java平台开源？<br />
<br />
A: I think it's a good thing. It goes a long way toward easing the concerns of the open-source and research communities about the legal aspects of working with the platform. In many ways, it's always been a pretty open platform: free access to the source code, heck, even the bug database. But it's nice that Sun has gone all the way.<br />
我认为那很好。在缓解开源和研究社区对工作于这个平台的法律方面的担心已经走过了很长一段路。在许多方面，它始终都是一个十分开放的平台：自由的访问源代码，甚至是Bug数据库。但非常好的是Sun已经完成了所有的工作。<br />
<br />
Q: What advice would you give to a programmer new to the Java language?<br />
对于一个新接触Java语言的程序员，你有什么建议？<br />
<br />
A: Write lots of code. Have fun with it! Collaborate with people who are more experienced than you and learn from them. Join an open-source project. Code reviews are a great way to learn. Don't be embarrassed when people find problems in your code. Fix them and have fun watching your code and your skills improve. Oh, yeah, and go buy a copy of Effective Java.<br />
写大量的代码，并以它们为乐。与那些比你更有经验的人一起协作，并向他们学习。加入一个开源项目。代码回顾是一个很好的学习方法。当别人发现你的代码中的问题时，不必感到尴尬。修复这些问题，并对于观看你的代码及对你技术的改进而感到高兴。噢，是的，去买一本Effective Java。<br />
<br />
Q: What do you think is the most inventive use of Java technology?<br />
你认为对于Java技术最具创造性的应用是什么？<br />
<br />
A: That's another tough one. I guess you can't go too far wrong with the Mars Rover. I was blown away by the images that it transmitted.<br />
那是另一个棘手的问题。我猜想你不可能像Mars Rover(译注：美国的火星漫游者飞行器，它的系统软件使用Java编写，这是Java发展历程中的一件大事)那样走得那么远。我为它发射时的画面所倾倒。<br />
<br />
Q: What's the biggest misconception about Java technology?<br />
对Java技术的最大误解是什么？<br />
<br />
A: What irks me most is that there are still people who think it's slow. That's ancient history. By J2SE 1.4, the platform was not appreciably slower than traditional compiled languages, and it just keeps getting faster. Both of the last two releases were impressive in this regard. You don't have to touch your program. Just download the new release, and your program will run significantly faster.<br />
最让我感到厌倦的是，仍然有人认为Java很慢。那已经很久以前的事了。到J2SE 1.4时，这个平台就一点都不比传统的编译型语言慢了，并且它还在变得更快。最近发布的两个平台在这一点上令人影响深刻。你不用修改你的程序，只要下载最新的版本，你的程序就会明显地运行地更加快。<br />
<br />
Q: The Java class that you couldn't live without is...?<br />
你离开就不能活的Java类是...？<br />
<br />
A: I'm going to have to say LinkedHashMap, even if I did write it. It combines the speed of HashMap with the predictability of a sorted map.<br />
我将不得不说就是LinkedHashMap，即使我写出了它。它使用一个排序的映射预览而结合了HashMap的速度。<br />
<br />
Q: What's your favorite Java technology book?<br />
你所喜欢的Java技术书籍是什么？<br />
<br />
A: Modesty prevents me from answering this one directly, so I'll plug one of my favorite programming books that isn't specific to the Java language. Anyone who cares at all about bit twiddling should get their hands on Henry S. Warren's Hacker's Delight. It's the Bible of bit twiddling.<br />
谦虚防止我直接地回答这个问题。所以我将指出我所喜欢的编程书籍之一并不特别关于Java语言。任何真正关注位运算的人都应该接触过Henry S. Warren的"Hacker's Delight"，它是</span><span style="font-size: 10pt;">位运算</span><span style="font-size: 10pt;">的"圣经"。<br />
<br />
Another book that every programmer should read is Jon Bentley's Programming Pearls. It's a great book about "programming in the small," as relevant today as when it was first published in 1986.<br />
另一本每位程序员都应该阅读的书就是Jon Bentley的"Programming Pearls"。这是一本伟大的关于"小型编程"的书，它对今天的价值就如同1986年它第一次出版时一样。<br />
<br />
Q: Can you describe the process of writing code?<br />
你能描述一下写代码的过程吗？<br />
<br />
A: For me, there are several distinct stages. The hardest part is figuring out what problem I'm really trying to solve. Once I do that, it's usually reasonably straightforward to envision the rough outlines of the solution. Then it's a "simple matter of programming" to make it real, including the tests, of course.<br />
对于我，有几种不同的情况。最困难的部分就是勾画出我真正需要去解决的问题。一旦我做到了，常常就能相当直接地想像出解决方案的粗略轮廓。<br />
<br />
Q: What's the next big technology revolution?<br />
下一个大的技术革命是什么？<br />
<br />
A: I'm really bad at predicting the future. It's clear that multicore machines will be mainstream in the very near future and that programs will have to change to take full advantage of them. I'm not betting on Software Transactional Memories (STMs) in the near term, but on high-quality multithreaded utilities, such as java.util.concurrent. Oh, yeah, and everything that isn't already on the web will be soon.<br />
在预知未来方面，我真正的很差。清楚地是，在非常近的未来多核机器将成为主流，程序将不得不进行改变以充分利用它们。我不是在为软件事务内存(STM)打赌，而是为高质量的多线程工具类，如java.util.concurrent。噢，是的，还没有在Web上的每一件东西都将很快成真。<br />
<br />
Q: What do you see as the most important Java technology API five years from now?<br />
从现在开始的五年中，你将如何看待最重要的Java技术API？<br />
<br />
A: The core APIs -- java.lang and java.util -- will still be very important. But java.util.concurrent and its offspring will be equally important.<br />
核心API--java.lang和java.util--仍然将十分的重要。但是java.util.concurrent和它的后续者将变得同样重要。<br />
<br />
Q: If you could work on a dream project, what would it be?<br />
如果你能在一个梦想的项目中工作，你希望它是什么？<br />
<br />
A: A brand-new Java-like language atop the same virtual machine that is not compatible with the Java programming language. Rather than shoving yet more features into this language, I want a fresh start, but I want to leverage much of the work that has already gone into the platform. That means using the same virtual machine and providing some interoperability story with existing libraries.<br />
一个崭新的、与Java相似的语言，运行在一个相同的但与Java程序设计语言不兼容的虚拟机中。不要把这些特性挤到这个语言中，我想要一个新的开始。我想影响大量的已经进入这个平台的工作。这就意味着，使用相同的虚拟机并为已有的类库提供交互性功能。<br />
<span style="font-style: italic;">关于Joshua对他所理想的项目的描述，我非常的迷惑，这段话仍然需要研究。</span><br />
<br />
Q: What recent changes to the platform have made your life easier?<br />
最近这个平台的什么变化使你的生活变得更容易。<br />
<br />
A: I hate to sound like a broken record, but java.util.concurrent. Also java.util.ArrayDeque and NavigableMap. The for-each loop is great, and so are generics, even if they do complicate things somewhat.<br />
我很不喜欢说类似于破记录之类的话，但对于java.util.concurrent要除外。java.util.ArrayDeque和NavigableMap也是的。For-Each循环很好，泛型也是如此，即使它们做了一些稍微复杂的工作。<br />
<br />
Q: Where in the process of programming do you have the most fun?<br />
编程中的哪个过程使你最有乐趣？<br />
<br />
A: I enjoy many phases, from requirements analysis to debugging. Each of these phases presents me with brainteasers, and solving these brainteasers is what I like best. But nothing makes me feel better than building a reusable component and seeing it get reused.<br />
我享受于许多的阶段，从需求分析到调试。这些阶段中的每一个都会呈现给我难题，而解决这些难题则是我最喜欢的。但没有什么比构建一个可重用的组件并看到它被重用更让我感到高兴了。<br />
<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/123283.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> 2007-06-11 08:33 <a href="http://www.blogjava.net/jiangshachina/archive/2007/06/11/123283.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Patterns are Code Smells(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/06/04/121731.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 04 Jun 2007 01:00:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/06/04/121731.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/121731.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/06/04/121731.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/121731.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/121731.html</trackback:ping><description><![CDATA[<div style="text-align: center;"><span style="font-weight: bold;"><span style="font-size: 14pt;"><span style="font-size: 14pt;">Design Patterns are Code Smells</span></span></span><br />
</div>
<span style="font-size: 10pt;">上个月就在TheSeverSide中看到关于<a href="http://www.relevancellc.com/2007/5/17/design-patterns-are-code-smells">这篇文章</a>(实际是一篇简短的Blog)的消息，当时就觉得很有趣，因为自己正在学习设计模式，故将这篇短文保存了下来。昨天在自己电脑中又看到了此文，顺手就把它翻译了出来。<br />
这篇Blog的作者认为大部分设计模式在代码层都是code smell，在文末评论中有Google Guice项目的leader -- Bob Lee的评语。Bob对作者的观点表示了赞同，但也指出框架可以减轻对模式的需要。(2007.06.04最后更新)<br />
<br />
在原始的Gof的书中，作者清楚地指出当你在使用设计模式时，<br />
&nbsp;&nbsp;&nbsp; 程序设计语言的选择是重要的，因为它会影响某人的观点。我们的模式假设是使用Smalltalk/C++语言级特性，这种选择决定了什么容易/不容易实现(Design Patterns, p.4)。<br />
不幸的是，这条信息基本上被丢弃了，程序员们时常将模式当作处方。Martin Fowler解释了区别：<br />
&nbsp;&nbsp;&nbsp; 处方趋于更加特别，经常关联于一个特别的语言和平台。甚至当一些模式需要依赖一个特定的平台时，他们仍试图于用这些模式去解释更加一般的概念。<br />
如果你已经遇到一个Java或C#应用程序看起来像C++的处理方式，你就会知道这两个概念的混合将造成损害。<br />
不管你如何从处方中区分出模式，你所思考的程序设计语言就是你要为之所设计的程序语言。这也就是为什么Prags鼓励每个人每年学习一种新的语言。你将仍会为你所知道的这组语言进行设计，但至少你将不会没有希望。<br />
程序语言提前使模式不能成为药方。回到1998年，Petter Norvig争论道，大部分的原始Gof模式在Dylan或Lisp中都是无形或简单的。之后，Greg Sullivan对Scheme持同样的观点。Jan Hannemann也使用Java+AspectJ证明了相同的观点。设计模式不能如处方那样发挥良好。它们最多是周期性的。<br />
在代码级别，大部分的设计模式都是代码异味(code smells)。当程序员在代码检查中看到了设计模式，他们就会滑入到催眠般的熟悉场景中。醒醒！那是一个设计模式，或者说是一个来自腐臭程序语言的失效药方？<br />
<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/121731.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> 2007-06-04 09:00 <a href="http://www.blogjava.net/jiangshachina/archive/2007/06/04/121731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Apache给Sun的一封公开信(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2007/04/18/111651.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 18 Apr 2007 07:45:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2007/04/18/111651.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/111651.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2007/04/18/111651.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/111651.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/111651.html</trackback:ping><description><![CDATA[<p style="font-size: 10pt;" align="center"><span style="font-size: 12pt;"><strong><span style="font-size: 14pt;">Apache给Sun的一封公开信</span></strong></span></p>
<p style="font-size: 10pt;" align="justify">上周(2007年4月10)，由于不满于Sun所提供的针对TCK的许可证，Apache发表了一封公开信。<br />
这个消息我首先是在TSS上看到，然后又在<a href="http://www.apache.org/jcp/sunopenletter.html">Apache</a>上阅读了公开信的全文，觉得很有意思，就翻译出来了。其实早有朋友翻译了该文并发表了出来，但我的这篇译文是独立翻译出来的，而且是本人第一次完整地翻译一篇文章。所以放在此处，也算是对自己的一个小小的鼓励吧 :-) (2007.04.18最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;在2007年4月10日，Apache软件基金会向Sun Microsystems公司发出一封如下的信，表达了他们对无力获得一个针对Java SE 5 Technology Compatibility Kit(TCK)的可接受的许可证的关切。Apache Harmony项目需要使用TCK去证明其自身是否兼容Java SE 5规范，这也是Sun对Java SE 5规范协议的要求。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;亲爱的Jonathan：<br />
&nbsp;&nbsp;&nbsp;&nbsp;我是Geir Magnusson Jr，也是Apache软件基金会(ASF)--一个非赢利性的公益机构--的官员。我负责ASF在Java Community Process (JCP)中的相关事务。同时，我也是Apache Harmony项目的副主席，并在此项目中代表ASF。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;从2006年8月开始，ASF就已经一直在尝试着从Sun那里确保获得一个可被接受的针对Java SE测试工具包的许可证。该测试工具包，被称作"Java Compatibility Kit"或"JCK"，Apache Harmony项目需要它去证明该项目是否兼容Java SE 规范，这也是Sun的规范协议所要求的。Sun正在提供的JCK许可证通过对我们软件的用户的"应用领域"的限制来强加知识产权的约束。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;这些约束对于我们而言是完全不能够被接受的。根据我下面的解释，这些约束违背了"Java规范参与协定(JSPA)"(JSPA是JCP的管理规则，Sun作为缔约的签字方也需要遵守)的条款。ASF在支持使商业软件繁荣的开源软件生态系统方面有着光荣的历史。然而，Sun的JCK许可证会防止Sun的商业Java应用成为ASF开源软件扩展的一部分。该许可证也会阻止我们的用户在特定领域的应用中使用Apache的软件。这种或明或暗的对知识财产侵犯的行径使得一个人就能够越过生态系统中的其它参与者而颠覆商业性的优点。在一个开放的生态系统中，通过实现一个规范而产生的必要知识财产要独立于该生态系统中某个成员的特别利益而被保护，这也是我们拒绝你所提出的条款的基础。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;你的约束违背了JCP的基本保护原则，这些保护原则确保了两条：<br />
&nbsp;&nbsp;&nbsp;&nbsp;a)规范的领导者与专家组制定开放的规范；b)任何人都可以实现这些规范，并分发这些实现品，而不必害怕为了实现这些规范需要对规范的领导或专家组成员承担任何"必要的知识财产"的责任。特别地，JSPA还要求如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;1)一个规范的领导者不能向获得许可证的人强加任何可能限制或约束他们创建或发行他们的独立实现品的合约性条件或约定。<br />
&nbsp;&nbsp;&nbsp;&nbsp;2)一个规范的领导者必须向任何兼容规范的实现品免费许可所有的"必要知识财产"。<br />
你的条款正试图绕过这两条要求。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;除了阻止了Harmony项目--一个从2005年就开始的由社区领导的ASF开源项目--之外，为了遵从你的合约性义务，这个错误将使作为开放标准组织的JCP的信誉和作为开放技术Java其本身的名誉置于危险境地。我们相信这也将威胁到通行的商业Java生态系统的合作本性，使Sun与ASF之间长期友好的关系，甚至是Sun与更广大的开源社区的关系，处于危险境地。而所有的这些都是Java持续增长的关键。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;除开JSPA的义务，这些限制也违背了Sun的公开承诺，即任何由Sun主导的规范对于开源或自由软件都是完全可实现的、可发行的。这还用不着提及"完全可实现的"(包括针对JCK)也是规范协议所要求的。最后，对我们的用户在使用领域方面的限制也违背了开源软件协议的基本原则，因此这些限制将阻止基于开源协议的发行行为，也包括我们自己的这些行为。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;我们对你所提供的协议的拒绝是明确而有效的。我们所面对的现状是与JCP基本知识财产哲学、作为基于开放标准生态系统的Java、Sun对自由和开源社区的公开承诺、Sun作为JSPA中规范的领导者的合约性义务，全面地冲突。JCP被清晰地设计成防止任何一个单独的参与者去展示它能够控制市场的秩序。另外，这也与"开源"的表意其及内涵相反。而"开源"是Sun所陈述的商业战略中的一个关键元素。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;通过Apache Harmony，ASF在规范的领导者Sun的理解下并出于良好的信念正在实现Java SE，这将实现双赢。我们的目的一直都是创建一个被认证的兼容的Java SE实现，该实现会在Apache许可证下发行。为了做到这些，我们需要JCK。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;我们希望你在30天内向我们提供一个可被接受的、符合JSPA的许可证，或者给出一个你们为什么不能那么做的公开解释。<br />
&nbsp;&nbsp;&nbsp; 我们正期盼你的回复。<br />
<br />
&nbsp;&nbsp;&nbsp; Geir Magnusson Jr.<br />
&nbsp;&nbsp;&nbsp; VP, Java Community Process<br />
&nbsp;&nbsp;&nbsp; Apache Software Foundation<br />
&nbsp;&nbsp;&nbsp; geirm at apache dot org</p>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/111651.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> 2007-04-18 15:45 <a href="http://www.blogjava.net/jiangshachina/archive/2007/04/18/111651.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>