﻿<?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/27200.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, 14 Dec 2014 21:01:28 GMT</lastBuildDate><pubDate>Sun, 14 Dec 2014 21:01:28 GMT</pubDate><ttl>60</ttl><item><title>利用Java SE 8流处理数据(I)(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 27 Jul 2014 12:54:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/416235.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/416235.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/416235.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">利用Java SE 8流处理数据</span></strong><br /><span style="font-size: 10pt;">-- 使用Java流操作去表达复杂的数据查询</span></div><br /><span style="font-size: 10pt;">本文是<a href="http://www.oracle.com/technetwork/java/javamagazine/index.html">Java Magazine</a> 201403/04刊中的一篇文章，也是文章系列"利用Java SE 8流处理数据"中的第一篇，它概述了Java流的基本原理与基本应用，是一篇很好的Java Streams API的入门文章。(2014.07.27最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 没有集合对象，你会怎么样？几乎每个Java应用都会创建并处理集合。它们是许多编程任务的基础：集合使你能够对数据进行分组和处理。例如，你也许会创建一个关于银行交易的集合，该集合代表了某个用户的银行流水清单。然后，你可能想要处理整个集合去算出该用户花了多少钱。尽管数据处理十分重要，但Java在此方面表现的远不完美。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 首先，典型的集合处理模式与类SQL操作相似，诸如"查找"(如找出最大金额的那笔交易)或者"分组"(如，将与购买杂货相关的交易进行分组)。大部分数据库允许你以声明的形式去指定这些操作。例如，后面的SQL查询会让你找到那笔最大金额交易的ID："SELECT id, MAX(value) from transctions"。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如你所见，我们并不需要去实现如何计算最大值(例如，使用循环和一个变量去追踪这个最大值)。我们仅需要表达什么是我们想要的。这种基本思想就意味着，你不太需要担心去显式地实现这些查询--它们已经为你处理好了。为什么我们不能在处理集合时也这样做呢？你发现自己有多少次都是在一遍又一遍地使用循环去重复实现这些操作呢？</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 其次，我们如何才能更高效地去处理大型集合？理想情况下，在加速进行处理时，你会想到利用多核架构。然而，编写并行程序既困难又容易出错。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java SE 8赶来帮忙了！Java API的设计者们在升级API时引入了一种新的称之为Java流(流)的抽象，它允许你以声明形式去处理数据。另外，Java流可以利用到多核架构而不必编写一行多线程代码。听起来不错，不是吗？这就是本文章系列所要探究的主题。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java流能为我们做些什么呢？在探究这些细节之前，让我们先看一个例子，这样你才能对这种新的使用Java SE 8 Java流的编程风格有感觉。假设我们要找到所有类型为grocery的交易并返回它们的ID列表，并按交易金额的递减顺序对该列表进行排序。在Java SE 7中，我们应该会把清单1所示的程序那样去做。而在Java SE 8中，我们则会像清单2所示的那样去实现。</span><br /><em><strong><span style="font-size: 10pt;">清单1</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;groceryTransactions&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Arraylist</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;t:&nbsp;transactions){<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY){<br />&nbsp;&nbsp;&nbsp;&nbsp;groceryTransactions.add(t);<br />&nbsp;&nbsp;}<br />}<br />Collections.sort(groceryTransactions,&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Comparator(){<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;compare(Transaction&nbsp;t1,&nbsp;Transaction&nbsp;t2){<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;t2.getValue().compareTo(t1.getValue());<br />&nbsp;&nbsp;}<br />});<br />List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;t:&nbsp;groceryTransactions){<br />&nbsp;&nbsp;transactionsIds.add(t.getId());<br />}</span></div><br /><em><strong><span style="font-size: 10pt;">清单2</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionsIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sorted(comparing(Transaction::getValue).reversed())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 图1形象地解释了那段Java SE 8程序。首先，我们调用List对象中的Java流()方法从交易列表(数据)中获取一个Java流对象。然后，多个操作(过滤，排序，映射，归集)链接在一起形成了一条线，这条线可以被看作构成了一条数据查询。</span><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140304_stream_01.png" alt="" border="0" height="144" width="505" /><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 那么如何并行地执行该程序呢？在Java SE 8中这很简单：只需要使用parallelJava流()方法去替换Java流()方法，如清单3所示。Java流 API会在内部对你的查询进行解构，并利用上你机器中的多核处理器。</span><br /><em><strong><span style="font-size: 10pt;">清单3</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionsIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.parallelStream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sorted(comparing(Transaction::getValue).reversed())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在该关于Java SE 8 Java流的文章系列结束时，你将能够使用Java流 API编写出像清单3那样的功能强大的查询程序。</span><br /><br /><strong><span style="font-size: 12pt;">Java流入门</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 让我们先从一点理论开始。Java流的定义是什么？一个简短的定义就是"来自于一个数据源的能够支持聚合操作的一串元素"。让我们把它拆开来说：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>一串元素</strong>：Java流为一串特定类型值的集合提供了一个接口。然后，Java流实际上并不存储元素，它们会在需要时被用上。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>数据源</strong>：Java流要使用一个提供数据的源，诸如集合对象，数组或I/O资源。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>聚合操作</strong>：Java流支持类SQL的操作，以及来自于函数编程语言的通用操作，诸如过滤，映射，归一，查找，匹配，排序，等等。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另外，与集合操作非常不同的是，Java流操作拥有两项基本特质：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 管道：许多Java流操作会返回它们自己，这就使得这些操作能够链接在一起以组成一个大型管道。这样就可以进行一些诸如惰性和短路之类的优化，后面我们会进行探究。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 内部遍历：集合是显式地进行遍历(外部遍历)，但不同于集合，Java流是在幕后进行遍历。让我们重新看看之前的示例代码来解释这些原理。图2形象地解释了清单2的更多细节。</span><br /><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javamagazine/20140304_stream_02.png" alt="" border="0" height="364" width="595" /><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 首先，通过调用Java流()方法，我们从交易列表中得到了一个Java流对象。那么数据源就是交易列表，它将向Java流中提供一串元素。然后，我们对该Java流应用了一系列的聚合操作：过滤(提供一个谓语去过滤元素)，排序(提供一个比较器去对元素进行排序)，以及映射(解析出信息)。所有的操作都会返回该Java流，以便能够链接这些操作去组成一个管道，这可被看作是对数据源的一个查询。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在调用collect()操作之前，没有实际工作会被执行。collect()方法将开始处理这个管道以返回一个结果(某个不是Java流的对象，在此处，是一个List对象)。现在还不需要去关注collect()方法，我们会在以后的文章去一探究竟。此时，你会发现collect会将各种数据加工方法作为参数，将收集到的Java流元素归结为一个结果。此处，toList()就描述了一个将Java流对象转化为List对象的加工方法。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在探究与Java流有关的各个方法之前，最好是停下来深入思考一下Java流和集合之间观念上的不同之处。</span><br /><br /><strong><span style="font-size: 12pt;">Java流 vs. 集合</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 已有的Java集合概念与新的Java流概念都为一串元素提供了接口。那它们有何不同吗？简单地说，集合是关于数据的，而Java流是关于计算的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 想想这种情况，一部存储在DVD中的电影。这就是一个集合(可能是字节，可能是帧--在此处，我们不必关心这些)，因为它包含有全部的数据结构。现在再想想这种情况，这部电影被转化成了数据流，通过互联网去观看它。此时它就是一个(字节或帧的)流。流视频播放器只需要下载一些晚于用户当前所观看位置的帧就可以了。这样，你就可以在大部分值被计算出来之前先展示流开头处的值(想想流化一场现场直播的足球比赛)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 粗看之，集合与流的区别就是与何时处理数据有关。集合是内存中的数据结构，它包含有当前数据结构中的全部值--将所有元素加入到集合之前，必须先对所有元素进行处理，相反地，Java流只是逻辑上固定的数据结构，它里面的元素只会根据需要进行处理。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用Collection接口，要求用户实现遍历(例如，使用增强的for循环，即foreach)；这被称之为外部循环。相反地，Stream类库使用内部遍历--它已经为你实现好了遍历，它会关心存储流的结果值的位置；你仅需要提供一个函数告诉它要做些什么就行了。清单4(对集合的外部遍历)和清单5(对Java流的内部遍历)中的代码形象地展示了这一不同之处。</span><br /><em><strong><span style="font-size: 10pt;">清单4</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Transaction&nbsp;t:&nbsp;transactions){<br />&nbsp;&nbsp;&nbsp;&nbsp;transactionIds.add(t.getId());<br />}</span></div><br /><strong><em><span style="font-size: 10pt;">清单5</span></em></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;transactionIds&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getId)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单4中，我们显式且顺序地遍历了交易列表，抽取了每个交易ID，然后将它加到一个收集器中。相反地，当使用流时，没有显式的遍历。清单5中的代码构建了一个查询，其中的map操作被设定为一个参数，它会抽取交易ID，然后collect操作会把结果Stream对象转化成一个List对象。</span><br /><span style="font-size: 10pt;">你现在应该知道什么是Java流，以及如何去使用它。现在让我们看看Java流所支持的操作之间的区别，这样你就能构建自己的数据查询了。</span><br /><strong><br /><span style="font-size: 12pt;">Java流操作：使用流去处理数据</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; java.util.stream.Stream接口定义了许多操作，它们可被归集为两类。在图1所示的例子中，你可以看到如下操作：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 过滤，排序和映射，它们可被连接在一起组成一个管道</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 收集，它关闭了这个管道并返回结果</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 能够被连接在一起的Java流操作被称为中间操作。这些操作之所以能被连接在一起，是因为它们都会返回Stream对象。这些操作从这个管道中返回结果，结果的类型可以是List，Integer，甚至是void(任何Stream以外的类型)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你也许很好奇为什么这种区别很重要。是这样的，在这个Java流管道的最终操作被调用之前，中间操作并不会执行任何处理；它们是"惰性"方法。这是因为中间方法经常会被"合并"，在最终操作中它们会被合成为单一的执行路径。</span><br /><em><strong><span style="font-size: 10pt;">清单6</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;numbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.asList(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">5</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">6</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">7</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">8</span><span style="color: #000000; ">);<br />List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;twoEvenSquares&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;numbers.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(n&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">filtering&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; "> n);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(n&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">mapping&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; "> n);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;n;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.limit(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">例如，思考下清单6中的程序，它是从给定的数列中计算奇数的平方。你可能会很惊讶，它打印出如下结果：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">filtering&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; "><br />filtering&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />mapping&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />filtering&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; "><br />filtering&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; "><br />mapping&nbsp;</span><span style="color: #000000; ">4</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这是因为limit(2)使用了短路；我们只需要处理流的一部分，而不是全部，去得到一个结果。这就类似于测评一个由and操作符关联起来的大型布尔表达式链：一旦某个表达式返回了false，那么就可以认为整个表达式链就是false，而不必测评所有的表达式了。在这个例子中，limit()方法将返回的Java流的长度限定为2。另外，filter与map被合并在了同一条执行路径中了。</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 归纳一下到目前为止，在使用Java流时我们所学到的内容，总言之，涉及三个方面：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个数据源(例如一个集合)，对它执行查询</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个中间操作的链，它组成一个流的管道</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个最终操作，它执行流的管道并产生结果</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 现在让我们看看Java流所支持的一些操作。参考java.util.stream.Stream接口可以得到这些方法的完整清单，再看看本文末尾所给出的资源，它包含有更多的例子。</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>过滤</strong>。有多种方法可以对流中的元素进行过滤：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; filter(Predicate)：使用一个谓语(java.util.function.Predicate)作为参数，它会返回一个包含所有匹配给定谓语条件元素的Java流。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; distinct：返回一个包含有唯一元素的Java流。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; limit(n)：返回的流的长度不能超过n。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; skip(n)：返回的流将不包括前n个元素。</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>查找与匹配</strong>。一个通用的数据处理模式就要考虑一些元素是否匹配给定的属性。你可以使用anyMatch，allMatch和noneMatch方法帮你做到这一点。这些方法都会使用一个谓语参数并返回boolean值作为结果(所以，它们是最终操作)。例如，使用allMatch去查出交易流中所有金额大于100的交易，如清单7所示。</span><br /><em><strong><span style="font-size: 10pt;">清单7</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;expensive&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.allMatch(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getValue()&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">100</span><span style="color: #000000; ">);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另外，Stream接口提供了方法findFirst和findAny，以取出流中任一元素。它们可以与其它的流操作，如filter，结合起来使用。findFirst和findAny都会返回一个Optinal对象(见清单8)。</span><br /><em><strong><span style="font-size: 10pt;">清单8</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Optional</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Transaction</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.findAny(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY);</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">Optional&lt;T&gt;类(java.util.Optional)是一个容器类，它代表一个存在或不存在的值。清单8中的程序，findAny方法可能没有找到任何类型为grocery的交易。Optional类包含多个方法去测试一个元素是否存在。例如，如果交易存在，通过使用ifPresent方法，我们可以选择一个操作去应用这个Optaional对象，如清单9所示(此处只是打印交易)。</span><br /><em><strong><span style="font-size: 10pt;">清单9</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.findAny(t&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;t.getType()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;Transaction.GROCERY)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.ifPresent(System.out::println);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>映射</strong>。Java流支持map方法，它使用一个函数(java.util.function.Function)作为参数，将流元素投影到其它形式。这个函数会被应用到每个元素，并将元素"映射"到新的元素。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 例如，你可能会想到使用它去抽取流中每个元素的信息。在清单10的例子中，我们返回了一个列表中每个字的长度。</span><br /><em><strong><span style="font-size: 10pt;">清单10</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;words&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.asList(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Oracle</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Java</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Magazine</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;wordLengths&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;words.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(String::length)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(toList());</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>归一</strong>。到目前为止，我们已见过的最终操作会返回boolean(allMatch等等)，void(forEach)或Optaional对象(findAny等等)。我们也使用collect方法将Stream对象中的所有元素放到一个List对象中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 然而，你也可以将流中的元素放到一个查询中，该查询可表达更为复杂的数据处理，例如"拥有最大ID"或者"算出所以交易金额的和"。这就可能对Java流用上reduce方法，该方法会对每个元素重复地应用一个操作(例如，加上两个数字)，直到生成结果。在函数式编程中，这常被称为折叠操作。因为该操作可被看作重复地"折叠"一张很长的纸(Stream对象)，直到这张纸的面积变得只有一点儿了。这就是折叠操作的结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 看看我们是如何使用循环去计算一个组数字的和会有助于理解这个问题：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;sum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;x&nbsp;:&nbsp;numbers)&nbsp;{<br />&nbsp;&nbsp;sum&nbsp;</span><span style="color: #000000; ">+=</span><span style="color: #000000; ">&nbsp;x;<br />}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 列表中的每一个数字元素都被迭代地组合在一起，并使用一个额外的操作符去产生结果。本质上，我们就是把一组数字"归一"成一个数字。在这段代码中有两个参数：数字和变量的初始值，即该例中的0，以及用于合并所有元素的操作符，即本例中的+。</span><br /><em><strong><span style="font-size: 10pt;">清单11</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;sum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;numbers.stream().reduce(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;(a,&nbsp;b)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;b);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 对Java流使用reduce方法，我们可以计算出流中的所有元素值之和，如清单11所示。reduce方法使用两个参数：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 初始值，0</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; BinaryOperation&lt;T&gt;，合并两个元素，并产生一个新值</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; reduce方法本质上就是重复应用模式的抽象。其它的查询，如"计算产量"或"计算最大值"(如清单12所示)则是reduce方法的特别实例。</span><br /><em><strong><span style="font-size: 10pt;">清单12</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;product&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;numbers.stream().reduce(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;(a,&nbsp;b)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;b);<br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;product&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;numbers.stream().reduce(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;Integer::max);</span></div><br /><strong><span style="font-size: 12pt;">数字流</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你已经看到可以使用reduce方法去计算整数流的和。然后，这也是有成本的：我们重复执行了许多拆箱操作以将Integer对象加到一起。如果我们能调用一个sum方法，使程序的意图更为明显，就像清单13那样，岂不是更好？</span><br /><em><strong><span style="font-size: 10pt;">清单13</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;statement&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(Transaction::getValue)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sum();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;error&nbsp;since&nbsp;Stream&nbsp;has&nbsp;no&nbsp;sum&nbsp;method</span></div><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">Java 8引入的三个特定的基本数据类型的流接口来应对这个问题--IntStream，DoubleStream和LongStream--它们专注于元素分别为int，double和long型的Java流。将一个流转化为特定类型的流，你最常使用的方法就是mapToInt，mapToDouble和mapToLong。这些方法与我们较早前看到的map方法是一样的，但它们会返回特定类型的Stream对象，而不是Stream&lt;T&gt;对象。例如，我们可以改进下清单13中的代码，如清单14所示那样。你也可以使用装箱操作将一个基本数据类型的流转化成一个使用包装对象的流。</span><br /><em><strong><span style="font-size: 10pt;">清单14</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;statementSum&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;transactions.stream()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.mapToInt(Transaction::getValue)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.sum();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;works!</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 最后，数字流的另一种有用的形式是数字区间。比如，你可能想生成介于1到100之间的所有数字。为了帮助生成这种区间，Java SE 8在IntStream，DoubleStream和LongStream中分别引入了两个静态方法：range和rangeClosed。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这两个方法都会使用两个参数，第一个参数是起始值，第二个参数是终止值。但是range方法生成的区间不会包含终止值本身，但rangeClosed生成的区间则会包含。清单15是一个使用rangeClosed方法的例子，它返回一个包含有全部介于10到30之间奇数的流。</span><br /><em><strong><span style="font-size: 10pt;">清单15</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">IntStream&nbsp;oddNumbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;IntStream.rangeClosed(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">30</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(n&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);</span></div><br /><strong><span style="font-size: 12pt;">构建流</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 有多种途径可以去构建一个流。你已经看过如何从集合对象中构建流。另外，我们还操控过数字流。你也可以从值，数组或文件中去创建流。另外，你甚至于可以从一个函数中生成无限流。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 可以直截了当地从值或数组中创建流：只需要使用一些静态方法即可，对于值，是Stream.of()；而对于数组，则要调用Arrays.stream()。如清单16所示。</span><br /><em><strong><span style="font-size: 10pt;">清单16</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Stream</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;numbersFromValues&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Stream.of(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">[]&nbsp;numbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">};<br />IntStream&nbsp;numbersFromArray&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.stream(numbers);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你也可以将一个文件转化为其内容行的流，使用静态方法Files.lines()即可。清单17就使用该方法计算了文件中行的数量。</span><br /><em><strong><span style="font-size: 10pt;">清单17</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;numberOfLines&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;Files.lines(Paths.get(&#8220;yourFile.txt&#8221;),&nbsp;Charset.defaultCharset())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.count();</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>无限流</strong>。最后，在总结本文之前，有一个令人非常兴奋的主意。到现在为止，你应该理解到流中的元素是按需生成的。有两个静态方法--Stream.iterate()和Stream.generate()--可以让你从一个函数中创建流。然而，因为被使用的元素是按需生成的，所以这两个方法可以"永远地"生成元素。这就是为什么我们称它为无限流：它就是没有固定大小的流，但它做的事情与一个从固定集合生成的流是一样的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单18就是一个使用iterate方法的例子，它会包含10的所有倍数。iterate方法使用一个起始值(此处的0)和一个Lambda表达式(类型为UnaryOperator&lt;T&gt;)去顺序地生成每一个新值。</span><br /><em><strong><span style="font-size: 10pt;">清单18</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Stream</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Integer</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;numbers&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Stream.iterate(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;n&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;n&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">10</span><span style="color: #000000; ">);</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我们也可以使用limit方法，以从一个无限流中得到一个固定流。如清单19所示，可以将流的长度限制为5。</span><br /><em><strong><span style="font-size: 10pt;">清单19</span></strong></em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">numbers.limit(</span><span style="color: #000000; ">5</span><span style="color: #000000; ">).forEach(System.out::println);&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;0,&nbsp;10,&nbsp;20,&nbsp;30,&nbsp;40</span></div><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java SE 8引入了Streams API，它让你能够表达更为复杂的数据处理查询。在本文中，你已见到流可以支持许多操作，诸如过滤，映射，归一和迭代，把它们结合在一起可以写出简洁的、更富表现力的数据处理查询。这种新的编程方法远不同于Java SE 8之前的集合处理。但是，它有许多好处。首先，它利用到了诸如惰性或短路这样的技术，以优化数据处理查询的性能。其次，能够自动地利用上多核架构，以并行地处理流。在本文章系统的第二部分中，我们将探索更高级的操作，例如flatMap和collect。请继续关注。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/416235.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2014-07-27 20:54 <a href="http://www.blogjava.net/jiangshachina/archive/2014/07/27/416235.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2014年04月美国非农业就业情况报告(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/05/05/413246.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 04 May 2014 16:12:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/05/05/413246.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/413246.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/05/05/413246.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/413246.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/413246.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">2014年04月美国非农业就业情况报告</span><br /></strong></div><span style="font-size: 10pt;">本文是上周五(2014.05.02)才发布的<a href="http://www.bls.gov/news.release/empsit.nr0.htm">美国2014年04月度非农业就业报告</a>中的概要部分，与软件技术毫无干系。这份报告在出台后的极短时间内对外汇市场造成了极大影响，纯粹是出于好奇看了一下这份报告，学到了不少英文行业名称，以及英文在数字方面的用法。(2014.05.05最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 今天，美国劳工统计署报告，4月份的全部非农业就业人数增加了288000，而且失业率降低了0.4个百分点，达到百分之6.3。就业增长的扩大受益于专业与商业服务业，零售业，餐饮业和建筑业。</span><br /><br /><strong><span style="font-size: 12pt;">家庭调查数据</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在4月份，失业率从百分之6.7降到了百分之6.3，失业人数则为980万，减少了73.3万。这两项统计展示了前4个月的动态。今年以来，失业率和失业人数分别减少了1.2个百分点和190万人。(详见表A-1)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在主要的劳动者分组中，4月的失业率分别下降了：成年男子(5.9%)，成年女子(5.7%)，青少年(19.1%)，白人(5.3%)，黑人(11.6%)，以及西班牙裔(7.3%)。亚裔的无业率为5.7%(未扣除季节性因素)，今年以来，该值的变化很小。(详见表A-1，A-2和A-3)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在4月里，再次失业者与新工作者分别减少了41.7万和12.6万。(再次失业者之前工作过，但在开始重新找工作之前他们并不算作劳动力；而新工作者是指之前从未工作过的人。)丢掉工作的和只完成临时工作的劳动者的数量减少了25.3万，达到520万。(详见表A-11)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 长期失业者(指失业时间达到或超过27周的人)的数量在4月份减少了28.7万，达到350万；这些个体占失业人数的35.3%。在过去的12个月里，长期失业者的数量已经减少了90.8万。(详见表A-12)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 随着民用劳动力在3月增长50.3万之后，民用劳动力在4月里减少了80.6万。在最近几个月里，就业参与率并没有显示出清晰的趋势，当前的值也去年10月的值是相同的。在过去这个月中，就业者与人口比例没有发生变化(58.9%)，但在过一年中略有变化。(详见表A-1)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 出于经济原因的兼职劳动者(有时候也称之为不情愿的兼职劳动者)的数量在4月略有变化，达到750万。这些个体之所以兼职是由于他们的工作时间被削减，或者是他们无法找到全职工作。(详见表A-8)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 4月份有220万准待业劳动力，比今年早些时候有轻微下降(该数据未扣除季节性因素)。这些个体并不算作劳动力，尽管他们愿意并且也可以去工作，而且在前12个月的某些时候也找过工作。但是他们并不被计算在失业者中，因为在该调查开始的前4周内，他们并没有去找工作。(详见表A-16)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在这些准待业者中，本月有78.3万就业信心丧失者，比今年早些时候略有变化(该数据未扣除季节性因素)。就业信心丧失者目前并没有去寻找工作，因为他们认为没有合适他们的工作。剩余的140万准待业者没有去寻找工作的原因则是诸如就学或家庭责任。(详见表A-16)</span><br /><br /><strong><span style="font-size: 12pt;">机构调查数据</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 全部的非农业就业者在4月份增长了28.8万。在前12个月中，平均每个月增长19.9万个工作岗位。就业增长在4月里得到了扩大，这得益于专业与商业服务业，零售业，餐饮业和建筑业的就业增长。(详见表B-1)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 专业与商业服务业在4月份增加了7.5万个就业岗位。在前12个月内，该行业每个月会平均增加5.5万个就业。在本月，该行业的就业增长仍在继续，其中临时辅助服务业(2.4+万)，公司与企业管理(1.2+万)，计算机系统设计及其相关服务业(0.9+万)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 零售业的就业岗位在本月增加了3.5万个。在过去的12个月中，该行业的就业已经增加了32.7万。在零售业中，本月的工作岗位增长发生在饮食商店(0.9+万)，日用百货商店(0.8+万)，汽车与零部件商(0.6+万)和非实体商店(0.4+万)。电子与电器商店在本月则丢失了1.1万工作岗位。批发业在过去一个月内增加了1.6万个工作岗位，而它在过去一年内增加了12.6万个工作岗位。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 餐饮业在本月增加的就业数(3.3+万)则与过去12个月的平均增长(2.8万/月)处于同一水平线上。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 建筑行业的就业在4月份增加了3.2万个，这些工作增长分别为重型和民用工程建筑业(1.1+万)和住宅建筑业(0.7+万)。在过去一年中，建筑业已经增加了18.9万个工作岗位，而且其中几乎四分之三的增长都发生在过去6个月中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 医疗行业本月增加了1.9万个岗位，与过去12个月的平均增长(1.7万/月)水平相持平。包括会员协会，个人服务业与洗衣业等其它服务性行业的就业在这个月内增加了15000个。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 采矿业在4月份增加了10000个工作岗位，大部分的增长都是在针对采矿的支持活动中(7000+)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 其它主要行业，包括制造业，运输与仓储业，信息服务业，金融服务，以及政府服务业，的就业在这个月内少有变化。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 全部私营非农业就业者的平均周工作时间没有变化，仍为34.5小时。制造业的周工作时间减少了0.2个小时，达到40.8个小时。工厂的加班时间没有变化，仍为3.5个小时。私营非农业就业者中的生产与非管理阶层员工的平均周工作时间也没有发生变化，依然为33.7个小时。(详见B-2和B-7)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本月，私营非农业就业者的平均时薪仍然为24.31美元。在过去12个月中，平均时薪已经增长了1.9%。私营的生产与非管理阶层员工的平均时薪则增长了3%，达到20.50美元。(详见表B-3和B-8)</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 二月份的全部的非农业就业岗位从19.7+万修正为22.2+万，而对于三月份的这一数据，则是从19.2+万修正为20.3+万。根据这些修正，二月份与三月份的就业增长比之前报告的要高出3.6万个。<br /><br /></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/413246.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2014-05-05 00:12 <a href="http://www.blogjava.net/jiangshachina/archive/2014/05/05/413246.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 8的语言变化(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 19 Apr 2014 15:48:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/412695.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/412695.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/412695.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java 8的语言变化</span></strong><br /><div align="center"><span style="font-size: 10pt;">--理解Lambda表达式和变化的接口类是如何使Java 8成为新的语言</span></div></div><span style="font-size: 10pt;">本文是IBM developerWorks中的一篇介绍Java 8关键新特性的<a href="http://www.ibm.com/developerworks/java/library/j-java8lambdas/index.html">文章</a>，它主要关注Lambda表达式和改进的接口。(2014.04.19最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8包含了一组重要的新的语言特性，使你能够更方便地构造程序。Lambda表达为内联的代码块定义了一种新的语法，给予你与匿名内部类相同的灵活性，但又没有那么多模板代码。接口的改变使得能够为已有接口加入新的特性，而不必打破现有代码的兼容性。了解这些语言变化是怎样一起工作的，请阅读本系列另一篇文章"Java 8并发基础"，可以看到如何在Java 8流中使用Lambda。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8的最大改变就是增加了对Lambda表达式的支持。Lambda表达式一种通过引用进行传递的代码块。它类似于某些其它语言的闭包：代码实现了一个功能，可以传入一个或多个参数，还可以返回一个结果值。闭包被定义在一个上下文中，它可以访问(在Lambda中是只读访问)上下文中的值。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如果你不熟悉闭包，也不必担心。Java 8的Lambda表达式是几乎每个Java开发者都熟悉的匿名内部类的一个高效版规范。如果你只想在一个位置实现一个接口，或是创建一个基类的子类时，匿名内部类为此提供了一种内联实现。Lambda表达式也用于相同的方式，但是它使用一种缩略的语法，使得这些实现比一个标准的内部类定义更为简洁。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在本文中，你将看到如何在不同的场景下使用Lambda表达式，并且你会学到与Java接口定义相关的扩展。在本文章的姊妹篇JVM并发系列的"Java 8并发基础"一文中，可以看到更多使用Lambda表达式的例子，包括在Java 8流特性中的应用。</span><br /><br /><strong><span style="font-size: 12pt;">进入Lambda</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Lambda表达式就是Java 8所称的函数接口的实现：一个接口只定义一个抽象方法。只定义一个抽象方法的限制是非常重要的，因为Lambda表达式的语法并不会使用方法名。相反，该表达式会使用动态类型识别(匹配参数和返回类型，很多动态语言都这么做)去保证提供的Lambda能够与期望的接口方法兼容。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单1所示的简单例子中，一个Lambda表达式被用来对Name实例进行排序。main()方法中的第一个代码块使用一个匿名内部类去实现Comparator&lt;Name&gt;接口，第二个语句块则使用Lambda表达式。</span><br /><strong><span style="font-size: 10pt;">清单1. 比较Lambda表达式与匿名内部类</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Name&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String&nbsp;firstName;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String&nbsp;lastName;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Name(String&nbsp;first,&nbsp;String&nbsp;last)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;first;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;last;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;only&nbsp;needed&nbsp;for&nbsp;chained&nbsp;comparator</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;getFirstName()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;firstName;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;only&nbsp;needed&nbsp;for&nbsp;chained&nbsp;comparator</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;getLastName()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;lastName;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;only&nbsp;needed&nbsp;for&nbsp;direct&nbsp;comparator&nbsp;(not&nbsp;for&nbsp;chained&nbsp;comparator)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;compareTo(Name&nbsp;other)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;diff&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;lastName.compareTo(other.lastName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(diff&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;diff&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;firstName.compareTo(other.firstName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;diff;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;NameSort&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;Name[]&nbsp;NAMES&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Name[]&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Name(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Sally</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Smith</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printNames(String&nbsp;caption,&nbsp;Name[]&nbsp;names)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;anonymous&nbsp;inner&nbsp;class</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Name[]&nbsp;copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Arrays.sort(copy,&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;compare(Name&nbsp;a,&nbsp;Name&nbsp;b)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;a.compareTo(b);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;anonymous&nbsp;inner&nbsp;class:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;lambda&nbsp;expression</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Arrays.sort(copy,&nbsp;(a,&nbsp;b)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;a.compareTo(b));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;lambda&nbsp;expression:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单1中，Lambda被用于取代匿名内部类。这种匿名内部类在应用中非常普遍，所以Lambda表达式很快就赢得了Java8程序员们的青睐。(在本例中，同时使用匿名内部类和Lambda表达式去实现Name类中的一个方法，以方便对这两种方法进行比较。如果在Lambda中对compareTo()方法进行内联的话，该表达式将会更加简洁。)</span><br /><br /><strong><span style="font-size: 12pt;">标准的函数式接口</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 为了应用Lambda，新的包java.util.function中定义了广泛的函数式接口。它们被归结为如下几个类别：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 函数：使用一个参数，基于参数的值返回结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 谓语：使用一个参数，基于参数的值返回布尔结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 双函数：使用两个参数，基于参数的值返回结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 供应器：不使用任何参数，但会返回结果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 消费者：使用一个参数，但不返回任何结果。</span><br /><span style="font-size: 10pt;">多数类别都包含多个不同的变体，以便能够作用于基本数据类型的参数和返回值。许多接口所定义的方法都可被用于组合对象，如清单2所示：</span><br /><strong><span style="font-size: 10pt;">清单2. 组合谓语</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;use&nbsp;predicate&nbsp;composition&nbsp;to&nbsp;remove&nbsp;matching&nbsp;names</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;list&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(Name&nbsp;name&nbsp;:&nbsp;NAMES)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;list.add(name);<br />}<br />Predicate</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;pred1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Sally</span><span style="color: #000000; ">"</span><span style="color: #000000; ">.equals(name.firstName);<br />Predicate</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;pred2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Queue</span><span style="color: #000000; ">"</span><span style="color: #000000; ">.equals(name.lastName);<br />list.removeIf(pred1.or(pred2));<br />printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;filtered&nbsp;by&nbsp;predicate:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;list.toArray(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Name[list.size()]));</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单2定义了一对Predicate&lt;Name&gt;变量，一个用于匹配名为Sally的名字，另一个用于匹配姓为Queue的名字。调用方法pred1.or(pred2)会构造一个组合谓语，该谓语先后使用了两个谓语，当它们中的任何一个返回true时，这个组合谓语就将返回true(这就相当于早期Java中的逻辑操作符||)。List.removeIf()方法就应用这个组合谓语去删除列表中的匹配名字。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8定义了许多有用的java.util.function包中接口的组合接口，但这种组合并不都是一样的。所有的谓语的变体(DoublePredicate，IntPredicate，LongPredicate和Predicate&lt;T&gt;)都定义了相同的组合与修改方法：and()，negate()和or()。但是Function&lt;T&gt;的基本数据类型变体就没有定义任何组合与修改方法。如果你拥有使用函数式编程语言的经验，那么你可能就发会发现这些不同之处和奇怪的忽略。</span><br /><br /><strong><span style="font-size: 12pt;">改变接口</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在Java 8中，接口(如清单1的Comparator)的结构已发生了改变，部分原因是为了让Lambda更好用。Java 8之前的接口只能定义常量，以及必须被实现的抽象方法。而Java 8中的接口则能够定义静态与默认方法。接口中的静态方法与抽象类中的静态方法是完全一样的。默认方法则更像旧式的接口方法，但提供了该方法的一个实现。该方法实现可用于该接口的实现类，除非它被实现类覆盖掉了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 默认方法的一个重要特性就是它可以被加入到已有接口中，但又不会破坏已使用了这些接口的代码的兼容性(除非已有代码恰巧使用了相同名字的方法，并且其目的与默认方法不同)。这是一个非常强大的功能，Java 8的设计者们利用这一特性为许多已有Java类库加入了对Lambda表达式的支持。清单3就展示了这样的一个例子，它是清单1中对名字进行排序的第三种实现方式。</span><br /><strong><span style="font-size: 10pt;">清单3. 键-提取比较器链</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;key-extractor&nbsp;lambdas</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.lastName);<br />comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;comp.thenComparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.firstName);<br />Arrays.sort(copy,&nbsp;comp);<br />printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;key&nbsp;extractor&nbsp;comparator:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单3首先展示了如何使用新的Comparator.comparing()静态方法去创建一个基于键-提取(Key-Extraction) Lambda的比较器(从技术上看，键-提取Lambda就是java.util.function.Function&lt;T,R&gt;接口的一个实例，它返回的比较器的类型适用于类型T，而提取的键的类型R则要实现Comparable接口)。它还展示了如何使用新的Comparator.thenComparing()默认方法去组合使用比较器，清单3就返回了一个新的比较器，它会先按姓排序，再按名排序。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你也许期望能够对比较器进行内联，如：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.lastName)<br />&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(name&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name.firstName);</span></div><span style="font-size: 10pt;">但不幸地是，Java 8的类型推导不允许这么做。为从静态方法中得到期望类型的结果，你需要为编译器提供更多的信息，可以使用如下任何一种形式：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;com1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing((Name&nbsp;name1)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name1.lastName)<br />&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(name2&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name2.firstName);<br />Comparator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;com2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Name,String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">comparing(name1&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name1.lastName)<br />&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(name2&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;name2.firstName);</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 第一种方式在Lambda表达式中加入参数的类型：(Name name1) -&gt; name1.lastName。有了这个辅助信息，编译才能知道下面它该做些什么。第二种方式是告诉编译器要传递给Function接口(在此处，该接口通过Lambda表达式实现)中comparing()方法的泛型变量T和R的类型。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 能够方便地构建比较器以及比较器链是Java 8中很有用的特性，但它的代价是增加了复杂度。Java 7的Comparator接口定义了两个方法(compare()方法，以及遍布于每个对象中的equals()方法)。而在Java 8中，该接口则定义了18个方法(除了原有的2个方法，还新加入了9个静态方法和7个默认方法)。你将发现，为了能够使用Lambda而造成的这种接口膨胀会重现于相当一部分Java标准类库中。</span><br /><br /><strong><span style="font-size: 12pt;">像Lambda那样使用已有方法</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如果一个存在的方法已经实现了你的需求，你可以直接使用一个方法引用对它进行传递。清单4展示了这种方法。</span><br /><strong><span style="font-size: 10pt;">清单4. 对已有方法使用Lambda</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;sort&nbsp;array&nbsp;using&nbsp;existing&nbsp;methods&nbsp;as&nbsp;lambdas</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">copy&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Arrays.copyOf(NAMES,&nbsp;NAMES.length);<br />comp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Comparator.comparing(Name::getLastName).thenComparing(Name::getFirstName);<br />Arrays.sort(copy,&nbsp;comp);<br />printNames(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Names&nbsp;sorted&nbsp;with&nbsp;existing&nbsp;methods&nbsp;as&nbsp;lambdas:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;copy);</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单4做着与清单3相同的事情，但它使用了已有方法。使用Java 8的形为"类名:方法名"的方法引用语法，你可以使用任意方法，就像Lambda表达式那样。其效果就与你定义一个Lambda表达式去调用该方法一样。对类的静态方法，特定对象或Lambda输入类型的实例方法(如在清单4中，getFirstName()和getLastName()方法就是Name类的实例方法)，以及类构造器，都可以使用方法引用。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 方法引用不仅方便，因为它们比使用Lambda表达式可能更高效，而且为编译器提供了更好的类型信息(这也就是为什么在上一节的Lambda中使用.thenComparing()构造Comparator会出现问题，而在清单4却能正常工作)。如果既可以使用对已有方法的方法引用，也可以使用Lambda表达式，请使用前者。</span><br /><br /><strong><span style="font-size: 12pt;">捕获与非捕获Lambda</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你在本文中已见过的Lambda表达式都是非捕获的，意即，它们都是把传入的值当作接口方法参数使用的简单Lambda表达式。Java 8的捕获Lambda表达式则是使用外围环境中的值。捕获Lambda类似于某些JVM语言(如Scala)使用的闭包，但Java 8的实现与之有所不同，因为来自在外围环境中的值必须声明为final。也就是说，这些值要么确实为final(就如同以前的Java版本中由匿名内部类所引用的值)，要么在外围环境中不会被修改。这一规范适用于Lambda表达式和匿名内部类。有一些方法可以绕过对值的final限制。例如，在Lambda中仅使用特定变量的当前值，你可以添加一个新的方法，把这些值作为方法参数，再将捕获的值(以恰当的接口引用这种形式)返回给Lambda。如果期望一个Lambda去修改外围环境中的值，那么可以用一个可修改的持有器类(Holder)对这些值进行包装。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 相比于捕获Lambda，可以更高效地处理非捕获Lambda，那是因为编译能够把它生成为类中的静态方法，而运行时环境可以直接内联的调用这些方法。捕获Lambda也许低效一些，但在相同上下文环境中它至少可以表现的和匿名内部类一样好。</span><br /><br /><strong><span style="font-size: 12pt;">幕后的Lambda</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Lambda表达式看起来像匿名内部类，但它们的实现方法不同。Java的内部类有很多构造器；每个内部类都会有一个字节码级别的独立类文件。这就会产生大量的重复代码(大部分是在常量池实体中)，类加载时会造成大量的运行时开销，哪怕只有少量的代码也会有如此后果。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8没有为Lambda生成独立的类文件，而是使用了在Java 7中引入的invokedynamic字节码指令。invokedynamic作用于一个启动方法，当该方法第一次被调用时它会转而去创建Lambda表达式的实现。然后，该实现会被返回并被直接调用。这样就避免了独立类文件带来的空间开销，以及加载类的大量运行时开销。确切地说，Lambda功能的实现被丢给了启动程序。目前Java 8生成的启动程序会在运行时为Lambda创建一个新类，但在将来会使用不同的方法去实现。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 8使用的优化使得通过invokedynamic指令实现的Lambda在实际中运行正常。多数其它的JVM语言，包括Scala (2.10.x)，都会为闭包使用编译器生成的内部类。在将来，这些语言可能会转而使用invokedynamic指令，以便利用到Java 8(及其后继版本)的优化。</span><br /><br /><strong><span style="font-size: 12pt;">Lambda的局限</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如在本文开始时我所提到的，Lambda表达式总是某些特殊函数式接口的实现。你可以仅把Lambda当作接口引用去传递，而对于其它的接口实现，你也可以只是把Lambda当作这些特定接口去使用。清单5展示了这种局限性，在该示例使用了一对相同的(名称除外)函数式接口。Java 8编译接受String::lenght来作为这两个接口的Lambda实现。但是，在一个Lambd表达式被定义为第一个接口的实例之后，它不能够用于第二个接口的实例。</span><br /><strong><span style="font-size: 10pt;">清单5. Lambda的局限</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;A&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;valueA(String&nbsp;s);<br />}<br /></span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;B&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;valueB(String&nbsp;s);<br />}<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;a&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;String::length;<br />&nbsp;&nbsp;&nbsp;&nbsp;B&nbsp;b&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;String::length;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;compiler&nbsp;error!<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;b&nbsp;=&nbsp;a;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;ClassCastException&nbsp;at&nbsp;runtime!<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;b&nbsp;=&nbsp;(B)a;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;works,&nbsp;using&nbsp;a&nbsp;method&nbsp;reference</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;a::valueA;<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(b.valueB(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">abc</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />}</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 任何对Java接口概念有所了解的人都不会对清单5中的程序感到惊讶，因为那就是Java接口一直所做的事情(除了最后一点，那是Java 8新引入的方法引用)。但是使用其它函数式编程语言，例如Scala，的开发者们则会认为接口的这种限制是不自然的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 函数式编程语言是用函数类型，而不是接口，去定义变量。在这些编程语言中会很普遍的使用高级函数：把函数作为参数传递给其它的函数，或者把函数当作值去返回。其结果就是你会得到比Lambda更为灵活的编程风格，这包括使用函数去组合其它函数以构建语句块的能力。因为Java 8没有定义函数类型，你不能使用这种方法去组合Lambda表达式。你可以组合接口(如清单3所示)，但只能是与Java 8中已写好的那些接口相关的特定接口。仅在新的java.util.function包内，就特殊设定了43个接口去使用Lambda。把它们加入到数以百计的已有接口中，你将看到这种方法在组合接口时总是会有严重的限制。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用接口而不是在向Java中引入函数类型是一个精妙的选择。这样就在防止对Java类库进行重大改动的同时也能够对已有类库使用Lambda表达式。它的坏作用就是对Java 8造成了极大的限制，它只能称为"接口编程"或是类函数式编程，而不是真正的函数式编程。但依靠JVM上其它语言，也包括函数式语言，的优点，这些限制并不可怕。</span><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Lambda是Java语言的最主要扩展，伴着它们的兄弟新特性--方法引用，随着程序被移植到Java 8，Lambda将很快成为所有Java开发者不可或缺的工具。当与Java 8流结合起来时，Lambda就特别有用。查看文章"<a href="http://www.ibm.com/developerworks/library/j-jvmc2/index.html">JVM并发: Java 8并发基础</a>"，可以了解到将Lambda和流结合起来使用是如何简化并发编程以及提高程序效率的。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/412695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2014-04-19 23:48 <a href="http://www.blogjava.net/jiangshachina/archive/2014/04/19/412695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java流的8个特性(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Fri, 28 Feb 2014 07:25:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/410455.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/410455.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/410455.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Java流的8个特性</span></strong></div><span style="font-size: 10pt;">本文是稍早前java.net推荐的一篇</span><a href="http://speling.shemnon.com/blog/2014/02/11/8-cool-things-about-java-streams/"><span style="font-size: 10pt;">博文</span></a><span style="font-size: 10pt;">，描述了Java流的8个有用的特性。(2014.03.07最后更新)</span><br /><br /><span style="font-size: 10pt;">Lamba表达式是Java8到目前为止最棒的特性。但我认为有一个秘密武器展示了Lambda这一"语法糖"在提高代码可读性和可写性方面是何其的强大。当你在改进代码的表现力时，那么在对代码的理解方面你就上升到了新的境界，这能使最笨拙的工作变得简单。</span><br /><br /><span style="font-size: 10pt;">是什么秘密武器呢？就是Java Stream API。最近我参与了一个在线比赛，就是在一个对性能要求较高的环境中简单地使用Java Stream。令我惊讶的是，这个API让编写主要的循环程序变得十分简单，而且能很好地适应我所做出的众多变化。下面就是我所学到的8个特性。</span><br /><br /><strong style="font-size: 10pt;">1. Java流不需要Lambda表达式</strong><br />尽管这个API确实从Lambda表达式中获准良多，但你并不必非得使用Lambda。你可以回过去使用匿名内部类，但为什么要这么做呢？较可能的场景是，使用一个方法引用(例如Integer::valueOf)，或者一个实例对象。使用方法引用可将复杂的多行逻辑置于循环体之外，就如你在优化一个hash set查找时所看到的。而实例对象可用于实现"四人帮"的策略模式。但请不要使用匿名内部类，除非你不得不这么做。<br /><br /><strong>2. 窥入流内进行调试</strong><br />你可以在流的任何位置放入你所想加进去的媒质，这个媒质称为peek。该操作使用了一个消费者对象，并期望不产生任何结果，因为Lambda一般只返回空。我喜欢把peek用于向系统发送调试信息，就如<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">.peek(System.out::println)<br />.peek(it&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;System.out.printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">it&nbsp;is&nbsp;%s%n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;it)</span></div><br /><strong>3. 流化随机成员</strong><br />流并不局限于集合或数组，甚至是固定链表。如果你能创建一个Iterator或Supplier Lambda来创建流中的值，然后你就可以使用类java.util.stream.StreamSupport中的方法来创建一个流了。可以设想一个使用持续测量值，如内存消耗量或网络吞量，来驱动的流。<br /><br /><strong>4. 流化随机数</strong><br />如果你正在寻找一个简单的随机数，例如可以通过java.util.Random，这个类现在有了三个新的set方法ints()，longs()和doubles()来创建流。这些方法的重载版本可以让你设置边界，随机种子以及流中随机数的总量。<br /><br /><strong>5. 流化I/O Reader</strong><br />Java程序员的另一个常见工作就是一行一行地解析文件。现在java.io.BufferedReader有了一个新方法lines()，它会将I/O流转化为一个字符串流，以便于流的处理。<br /><br /><strong>6. 流化文件树</strong><br />如果访问的文件并不是你的菜，那就试试访问一个文件树会怎么样？类java.nio.file.Files中有几个方法都可以返回流。list()方法将列出一个目录下的所有文件，walk()方法将会递归地做到这一点，而filter()方法也会递归地访问这些文件，但会使用一些属性来进行过滤(当你有一个Path对象，有些事情会变得复杂起来)。你依然可以使用lines(Path)方法来通过流去获取内容。<br /><br /><strong>7. 流化复杂文本</strong><br />如果你依然念念不忘文本处理，但内容并不是基于行，那么就可以在java.util.regex.Pattern实例中使用splitAsStream(CharSequence)方法。这对于处理有数百万列的CSV文件或CLASSPATH十分有用。<br /><br /><strong>8. 流化ZIP文件</strong><br />说到对长CLASSPATH的搜索，你也可以很简单地调用名为stream的方法来流化java.util.zip.ZipFiles和java.util.jar.JarFiles，它会相应地返回一个ZipEntry或JarEntry实例。<br /><br />如果你都已经干过这些事了，那么你肯定知道它们并不是Java流的基本用途。不过将来会有足够多的博文去涉及Java流的基础。我只是认为上述这些都是被掩藏起来的宝藏，它们揭示了Java流的潜质。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/410455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2014-02-28 15:25 <a href="http://www.blogjava.net/jiangshachina/archive/2014/02/28/410455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Gradle构建Java Web应用(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 23 Jan 2014 13:22:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/409285.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/409285.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/409285.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><span style="font-size: 14pt;">使用Gradle构建Java Web应用</span></div>
<span style="font-size: 10pt;">本文是发布在<a href="http://www.java.net/">java.net</a>上的一篇摘自于&lt;Gradle in Action&gt;一书中的<a href="http://weblogs.java.net/blog/manningpubs/archive/2013/03/16/building-java-web-application-gradle">节选</a>，介绍了使用<a href="http://www.gradle.org/">Gradle</a>构建Java Web应用的过程。刚刚接触Gradle，看到了这篇小文，随手译了出来:-) (2014.01.23最后更新)</span><br />
<br />
<span style="font-size: 10pt;">当今世界，一派繁忙。在职业生涯和私人生活中，我们中间的许多人要同时管理多个项目。你可能常常发现自己处于不知所措及失控的状态。保持规整并专注于价值的关键是一个维护良好的工作清单。当然，你可能总是把你的任务写在一张纸上，但是你也许不可能在你所处的任何地方都可方便地获得这些工作条目？对互联网的访问几乎是无处不在的，无论是通过你的移动电话，还是公共的网络接入点。在&lt;Gradle in Action&gt;一书中，如图1所示的说明性示例是一个很有吸引力的可视化Web应用。</span><br />
<img src="https://www.java.net/sites/default/files/gradle001.jpg" alt="" />
<span style="font-size: 10pt;"><br />
<strong>图1 To Do应用可以通过互联网进行访问，并使用它去管理数据存储中的工作条目</strong></span><br />
<br />
<span style="font-size: 10pt;">Gradle插件表现的如同一个使能器，它会自动地执行这些任务。一个插件通过引入特定领域的规范以及对缺省值敏感的任务去对工程进行扩展。随Gradle发布的插件之一就是Java插件。该Java插件绝不仅仅是提供了源码编译和打包这样的基础功能。它为工程建立了一整套标准的目录布局，它会确保以正确的顺序去执行任务，这样，这些任务在Java工程环境中才是有意义的。现在是时候为我们的应用去创建一个构建脚本并去使用这个Java插件了。</span><br />
<br />
<strong><span style="font-size: 12pt;">构建Java应用</span></strong><br />
<span style="font-size: 10pt;">一开始，每个Gradle工程都会创建一个名为build.gradle的构建脚本。为了创建该脚本，并告诉该工程使用Java插件，应该像这样去做：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">apply&nbsp;plugin:&nbsp;'java'</span></div>
<span style="font-size: 10pt;">为了构建你的Java代码，一行代码就够了。但Gradle怎么知道去哪儿找你的源文件呢？该Java插件引入的规范之一就是源代码的路径。默认地，该插件会到目录src/main/java中搜寻产品的源代码。</span><br />
<br />
<strong><span style="font-size: 12pt;">构建Web应用</span></strong><br />
<span style="font-size: 10pt;">通过War插件，Gradle也提供了构建Web应用的扩展支持。War插件扩展了Java插件，它加入了针对Web应用程序开发的规范，并支持归集WAR文件。让我们也在这个工程中用用War插件：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">apply&nbsp;plugin:&nbsp;'war'</span></div>
<span style="font-size: 10pt;">Web应用源文件的默认路径是src/main/webapp。假设你已经明确了该应用所必要的Java类。那么要使产品的全部源代码和Web资源文件处于正确路径下，该工程的目录布局应该像下面这样：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">.<br />
</span><span style="color: #000000; font-family: Courier;">&#9500;&#9472;&#9472;&nbsp;build.gradle<br />
</span><span style="color: #000000; font-family: Courier;">&#9492;&#9472;&#9472;&nbsp;src<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;main<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;com<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;manning<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;gia<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;model<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoItem.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;repository<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;InMemoryToDoRepository.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoRepository.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;web<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoServlet.java<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#A<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;WEB-INF<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;web.xml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#B<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;css&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#C<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;base.css<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;bg.png<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;jsp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#D<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;index.jsp<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo-list.jsp<br />
<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;Web源文件默认目录<br />
</span><span style="color: #000000; font-family: Courier;">#B&nbsp;Web应用描述符文件<br />
</span><span style="color: #000000; font-family: Courier;">#C&nbsp;存储描述如何展现HTML元素的样式单文件的目录<br />
</span><span style="color: #000000; font-family: Courier;">#D&nbsp;存放JSP形式的动态脚本化视图组件</span></div>
<br />
<strong><span style="font-size: 12pt;">声明外部依赖</span></strong><br />
<span style="font-size: 10pt;">在实现这个Web应用的过程，我们使用的一些类，例如javax.servlet.HttpServlet，并非Java标准版(Java SE)的一部分。在构建工程之前，我们需要确保已经声明了这些外部依赖。在Java系统中，依赖类库是以JAR文件的形式去发布和使用的。许多类库可以从仓库，如一个文件系统或中央服务器，中获得。为了使用依赖，Gradle要求你至少定义一个仓库。出于一些考虑，我们将使用公共的可通过互联网进行访问的Maven Central仓库。</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">repositories&nbsp;{<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;mavenCentral()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #A<br />
</span><span style="color: #000000; font-family: Courier;">}<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;通过http://repo1.maven.org/maven2访问Maven2中央仓库的简短标记</span></div>
<span style="font-size: 10pt;">在Gradle中，依赖是通过配置项来进行分组的。我们将来Servlet依赖使用的配置项是providedCompile。该配置项用于那些在编译时而非运行时所需的依赖。像JSTL这样的运行时依赖，在编译时不会被用到，但在运行时则会被用到。它们都会成为WAR文件的一部分。下面的配置语句块声明了我们应用所需的外部类库：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">dependencies&nbsp;{<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;providedCompile&nbsp;'javax.servlet:servlet-api:</span><span style="color: #000000; font-family: Courier;">2.5</span><span style="color: #000000; font-family: Courier;">'<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;runtime&nbsp;'javax.servlet:jstl:</span><span style="color: #000000; font-family: Courier;">1.1.2</span><span style="color: #000000; font-family: Courier;">'<br />
</span><span style="color: #000000; font-family: Courier;">}</span></div>
<br />
<strong><span style="font-size: 12pt;">构建工程</span></strong><br />
<span style="font-size: 10pt;">我们已经准备好构建这个工程了。另到工程中的一个Java插件任务名为build。该任务将编译源代码，运行测试程序并归集WAR文件--所有的这些任务都将以正确的顺序被执行。执行命令gradle build之后，你可能会得到形如下面的输出：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">$&nbsp;gradle&nbsp;build<br />
</span><span style="color: #000000; font-family: Courier;">:compileJava&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #A<br />
</span><span style="color: #000000; font-family: Courier;">:processResources&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:classes<br />
</span><span style="color: #000000; font-family: Courier;">:war&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #B<br />
</span><span style="color: #000000; font-family: Courier;">:assemble<br />
</span><span style="color: #000000; font-family: Courier;">:compileTestJava&nbsp;UP-TO-DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #C<br />
</span><span style="color: #000000; font-family: Courier;">:processTestResources&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:testClasses&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:test&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #D<br />
</span><span style="color: #000000; font-family: Courier;">:check<br />
</span><span style="color: #000000; font-family: Courier;">:build<br />
<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;编译产品的Java源代码<br />
</span><span style="color: #000000; font-family: Courier;">#B&nbsp;War插件提供的任务，用于归集WAR文件<br />
</span><span style="color: #000000; font-family: Courier;">#C&nbsp;编译Java测试源代码<br />
</span><span style="color: #000000; font-family: Courier;">#D&nbsp;运行单元测试</span></div>
<br />
<span style="font-size: 10pt;">上述输出的每一行都代表执行了一个由Java或War插件提供的任务。你可能会注意到，有一些任务被标记为UP-TO-DATE。它的意思是指该任务被跳过去了。Gradle的增量构建支持策略会自动识别不需要执行的工作。特别是在大型商业项目中，该特性会极大地节省时间。</span><br />
<span style="font-size: 10pt;">在该工程的根节目录中，你将会发现一个名为build的子目录，它包含有执行构建之后的全部输出，包括类文件，测试报告，归集的WAR文件，以及像manifest这样的在打包时需要的临时文件。如下就是执行构建工作之后的工程目录结构：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">.<br />
</span><span style="color: #000000; font-family: Courier;">&#9500;&#9472;&#9472;&nbsp;build<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;classes<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #A<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;com<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;manning<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;gia<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;model<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoItem.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;repository<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;InMemoryToDoRepository.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoRepository.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;web<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;ToDoServlet$ToDoListStats.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;ToDoServlet.class<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;dependency-cache<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;libs<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;todo-webapp.war&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #B<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;reports<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;tests<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;base-style.css<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;css3-pie-</span><span style="color: #000000; font-family: Courier;">1</span><span style="color: #000000; font-family: Courier;">.0beta3.htc<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;index.html<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;report.js<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;style.css<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9500;&#9472;&#9472;&nbsp;test-results<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;binary<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;test<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;results.bin<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;tmp<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;war<br />
</span><span style="color: #000000; font-family: Courier;">&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#9492;&#9472;&#9472;&nbsp;MANIFEST.MF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #C<br />
</span><span style="color: #000000; font-family: Courier;">&#9500;&#9472;&#9472;&nbsp;build.gradle<br />
</span><span style="color: #000000; font-family: Courier;">&#9492;&#9472;&#9472;&nbsp;src<br />
<br />
</span><span style="color: #000000; font-family: Courier;">#A&nbsp;包含Java类文件的默认目录<br />
</span><span style="color: #000000; font-family: Courier;">#B&nbsp;归集的WAR文件<br />
</span><span style="color: #000000; font-family: Courier;">#C&nbsp;用于WAR的临时manifest文件</span></div>
<span style="font-size: 10pt;">你已经知道如何从一个基于标准目录结构的Web工程去构建WAR文件。现在是时候将它布署到一个Servlet容器中去了。在下一节中，我们将在本地开发机器中启动Jetty去运行这个Web应用。</span><br />
<br />
<strong><span style="font-size: 12pt;">运行应用</span></strong><br />
<span style="font-size: 10pt;">在本地机器中运行一个Web应用应该很容易，能够实践快速应用开发(RAD)，并能够提供快速的启动时间。最棒地是，它不要求你部署一个Web容器运行时环境。Jetty一个流行的轻量级开源Web容器，它支持前面提到的所有特性。在这个Web应用中加入一个HTTP模块，它就变成了一个嵌入式实现。Gradle的Jetty插件扩展了War插件，它提供的任务可以将一个Web应用部署到嵌入式容器中，并能够启动该应用。在你的构建脚本中，可以像如下那样使用这个插件：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">apply&nbsp;plugin:&nbsp;'jetty'</span></div>
<span style="font-size: 10pt;">这个将被我们用于启动Web应用的任务名为jettyRun。它甚至可以在无需创建WAR文件的情况下启动一个Jetty容器。执行上述命令后会得到如下形式的输出：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">$&nbsp;gradle&nbsp;jettyRun<br />
</span><span style="color: #000000; font-family: Courier;">:compileJava<br />
</span><span style="color: #000000; font-family: Courier;">:processResources&nbsp;UP-TO-DATE<br />
</span><span style="color: #000000; font-family: Courier;">:classes<br />
</span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Building&nbsp;&gt;&nbsp;:jettyRun&nbsp;&gt;&nbsp;Running&nbsp;at&nbsp;http://localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; font-family: Courier;">/todo-webapp-jetty</span></div>
<span style="font-size: 10pt;">在上述输出的最后一行中，该插件告诉了你Jetty即将侦听的请求地址。打开一个你喜欢的浏览器，并输入上述地址。最后，我们会看到这个To Do Web应用的行为。图2展示在一个浏览器中查看到该应用界面的截屏。</span><br />
<img src="https://www.java.net/sites/default/files/gradle002.jpg" alt="" />
<br />
<strong><span style="font-size: 10pt;">图2 To Do应用的Web界面及其行为</span></strong><br />
<span style="font-size: 10pt;"><br />在你通过组合键CTRL+C去停止这个应用之前，Gradle会让它一直运行。Jetty如何知道使用哪个端口和上下文环境去运行这个Web应用？再说一遍，这就是规范。Jetty运行Web应用所使用的默认端口就是8080。</span><br />
<br />
<strong><span style="font-size: 12pt;">总结</span></strong><br />
<span style="font-size: 10pt;">只需要较少的努力，你就可以使用Gradle去构建并运行一个Java Web应用。只要你严格遵循标准目录结构，那么你的构建脚本仅需要两行代码。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/409285.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2014-01-23 21:22 <a href="http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Concurrent Animated(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 07 Dec 2013 09:45:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/407310.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/407310.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/407310.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java Concurrent Animated</span></strong></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在最新一期的<a href="http://www.oracle.com/technetwork/java/javamagazine/index.html">Java Magazine</a>中有一篇访谈，介绍了一个学习Java并发编程的动画应用<a href="http://sourceforge.net/projects/javaconcurrenta/">Java Concurrent Animated</a>。该应用以十分直观的方式展示了Java并发工具包中的每一个重要组件，降低了学习Java并发编程的难度。(2013.12.07最后更新)</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：有多少人已经试用过了你的Java Concurrent Animated应用？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：该应用是在2009年7月被引入的，从那时算起，已经有了大约20000的下载量。但考虑到已有约一千万的Java开发者，这个下载量才只是开始。按国家区分，下载最多的分别是美国(23%)，印度(14)和中国(7%)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你可以下载一个可以执行的JAR文件，然后仅需双击它就可以运行了。该应用是由菜单驱动的，或者也可以使用向上或向下键在不同的图像和动画之间进行导航。它能运行在诸如Windows，Mac，Linux等等所有的平台上。它要求安装Java SE 6或更高的版本。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：对这个应用最典型的反馈是什么？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：大家告诉我这个工具很好用。许多人确实对此感到兴奋，尤其是那些正试图向团队教授合适并发技术的老师与领导们。Java是最早在核心类库中引入并发的语言之一。在当时，这是一个很强大的特性，但我们很快就发现一个非常优秀的程序员与会写出很糟糕的并发代码。进行恰当的并发编程是一件困难甚至是不可能的事情，但是如何人们能花些时间去理解一些现有的框架，那么在进行并发编码时所产生潜在错误就会变得极少。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 例如，去看看Java内存模型。开发者经常忽视Java内存模型，而像个幸福的傻瓜一样在编码，那么他们的程序会不太正常，因为Java虚拟机(JVM)和服务器可能无法利用到由Java内存模型所提供的优化。由于内核在速度与数量上都有了增长，厂商们期望能够高效地利用到这些内核，然而由于错误的并发管理，本来如期运行的程序却开始遇到了一些零星的错误。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你是说，这个应用会以我们所虚构的方式去使开发者们能够更快且直观地掌握Java并发的原理与实践？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：那是达到这一目的一个有趣的途径。你知道的，Java Concurrent Animated并不是一个Flash动画。它是一组可交互的Java程序，也即，每个动画都是真地在使用它所要演示的并发组件。在屏幕的右边是一个展示代码片断的面板，由于动画的运行，它会动态地高亮显示及恢复正在执行的代码。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 让你给你一个例子，这个例子发生在ReadWriteLock这个动画中。ReadWriteLock用于确保数据的一致性。它允许不受数量限制的线程去获取读锁，并能并发地对这个锁进行操作。但是，写线程在获取这个锁之前只能等待所有的读线程执行结束。一旦一个写线程获得了这个锁，那么其它的读线程或写线程将无法获取它。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 假设一个写线程正在等待正在执行中的读线程去释放这个读锁，但突然一个新的读线程跑过来了。那么谁应该获得这个锁会比较好呢？这个新的读线程应该跑到写线程前面去吗？毕竟，如果其它的读线程已经获得了这个锁，那么新来的读线程为什么要去等一个尚在等待中的写线程呢？而这实际上这正是Java 5所干的事儿。但某次我在Java 6上运行这个动画时，我注意到行为发生了改变。即，随后而来的读线程在获取到这个锁之前可能要等待所有的写线程先释放锁。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我认为这个新的行为是一个BUG，且向并发专家Heinz Kabutz博士提及了此事。博士解释道，这不是一个错误，而一个特性。如果允许新到的读线程跳到正处于等待中的写线程的前面去，这就存在产生线程饥饿条件的高风险。因为，存在一种很大的可能性，可能没有任何写线程能获得这个锁，它们将永远等待着。这就是一个如何使用动画去警示依赖于JVM运行时版本的线程行为的例子。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：以动画教程的形式来展示特殊值，在Java并发编程中有何与众不同吗？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：Miller定律教会我们，我们的大脑在某一时刻能处理的思维的数量是有限的。人类大脑倾向于进行顺序的思维处理，那么即便我们能够克服身体上的束缚，并能够去正确地进行理解，在以后也很难返回至前去重新构造前面的思维处理。可以肯定地是，如果另一个开发者在以后能深入对其进行研究，那么仍然非常难以从原有的思维成果中再次捕捉到认知轨迹。这样的话，脆弱的代码就会很突然地不能正常工作了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 通过使用框架，我们不仅将并发编程委托给了创建和维护该框架的聪明开发者们，而且还为沟通设计时引入了一个词典。所以，我可以说，&#8220;下面的代码会当作CyclicBarrier去执行&#8221;，而人们会明白那是什么意思。通过为java.util.concurrent中的所有组件都引入一个可交互化的动画应用，开发者们点着鼠标就能很方便地将他们所探究的功能进行可视化，使理解这些算法变得真心简单了。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你当时正在研究某些直觉，这些直觉可以帮助更方便地学习并发编程。从开发者的反馈来看，这些直觉看起来是有效的。</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：是的。例如，我前面解释的ReadWriteLock基本功能。读者们可能理解了，也可能没有。现在让我们看看这个与其有关的动画，如图表1所示。</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_01.PNG" height="568" width="872" /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 绿色线程是读线程，最上面的白色线程(带着菱形箭头)是一个写线程，它下面的白色线程是一个新的读线程，该线程在获取锁之前必须要等待所有的读线程与写线程执行完毕。如果你点击按钮并观看这些动画，会比通过浏览繁冗的解释性文字去进行理解要简单得多了。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：Heinz Kabutz评论道，Java被构建成能够一次性做许多事情，而这正与并发完全相关。你的学习系统是如何提高程序员的技能，以便他们能降低并发错误的风险。</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：经常地，当我要努力克服一个并发问题时，我知道解决方案就存在于某个设计模式中，但是哪一个呢？在开发者探寻一个正确解决方案时，Java Concurrent Animated为他们提供了一个所有方案的目录；在激发出正确方案的过程中，它扮演着向导的角色。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：当你管理的团队正在使用Java并发，并且你和你的团队都想更好地去理解Java并发，Java Concurrent Animated有着它的出发点。是什么导致你使用动画呢，能描述下这个过程吗？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：我的培训是针对投资部门的服务器端Java应用，在那里，并发是一个通常都会受到关注的问题。交易员们要求延迟要低，这样可以确保他们在这个需要于一毫秒窗口时间内捕捉交易机会的比赛中不会成为失败者。批量处理也要求快速完成，等等。所以我开始看到那些可怕的只写(write-only)组件，这些组件使人们在并发编程挣扎着。我自己也身处其中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 某天下午，我正坐在机场内，将要前往芝加哥为我的团队做一个关于并发的讲演。我正对讲演的PPT进行最后的处理，那组幻灯片着重演示了每一个重要的组件。为了引导我浏览java.util.concurrent中每个并发组件的状态，我写了一些状态机，它们展示了一些供我参考用的简单文本消息。在之前的生涯中，我曾在一家互联网创业公司中开发交互式的游戏，所以我懂得许多与动画相关的知识。这使我想到可以将PPT替换成一组交互式的动画应用，那会更为直观。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在等飞机的过程中，我写了一个初步的动画引擎，然后在我的状态机中调用了这个引擎。到了第二天早晨，我已经有一个可用的原型程序。多年来，我一直致力于这个框架，并且吸引了其他专家的建议。我传递过一份早期版本给Brian Goetz，令人惊讶的是，他为每个动画程序都给出了建议。我将他的所有建议到吸收到了该框架中。在我的第一次JavaOne讲演中，Kirk Pepperdine加入了进来。他建议为动画应用在真正的PPT中加入描述，以便讲演者能记住正在讨论的内容。随后我加上那些描述，这确实非常有用--不只是对讲演者有用，对于终端用户也很有用。Heinz Kabutz也加入了那场讲演，并建议修改某些动画，以使它们更为直观。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在另一场讲演中，一个很有激情的软件咨询师Oliver Zeigermann指出，很显然缺少了针对ConcurrentHashMap的动画。我问他是否有兴趣贡献这个动画，随后他添加了那个很有价值的动画程序。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你能带着我们过一遍Java并发工具包中的类吗？并能否解释一下这些动画程序是如何使开发者们更易于深入理解这些类？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：好的，但在没有动画程序的情况下确实很难办到。让我们看看CyclicBarrier，它有两个重要的状态，如图2和图3所示，它们展示了一个障碍和四个成员。在图2中，我们可以看到有三个成员已经到了，所以它们被阻止继续前进。图3展示了，一旦第四个成员也到达了，每个成员又可以向前走了。</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_02.PNG" height="569" width="873" /><br /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_03.PNG" height="570" width="872" /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这就形象地诠释了障碍的概念，亦即，在所有成员到达障碍点之前，每个成员必须等待。随着并发组件复杂度的增加--例如Fork/Join的动画，以及那些演示原生的wait和notify机制的动画--使用动画程序的好处就更不肖说了。</span><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：谈谈在创建这些动画程序的过程中所遇到的一些挑战。</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：有一些挑战。开始时，线程被表示成箭头。对于多数并发组件，这种表示法是有效的。后来我们必须提供一个可视化方案，不仅要表示线程，还要表示BlockingQueue中的对象。所以，我不得不引入一个称之为"精灵类型(sprite-type)"的概念，然后我们有了一个箭头型的精灵类型和一个新的"对象"型的精灵类型。后来，ConcurrentHashMap和AtomicInteger又需要新的精灵类型，因为我们试图要对他们的计算与交换行为进行可视化。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 后面又来了Fork/Join，新的挑战就是如何去表现那些完全不同于现有框架所表现的可视化部件。还有一个挑战，即Fork/Join动画需要解决一个实际的问题，但这个动画应该解决一个什么样的问题呢？</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开始时，我让这个动画程序去求Fibonacci数列，但却行不通。我在这个问题上纠结了两天时间，直到我认识到Fibonacci数列(Fn+1=Fn+Fn-1)无法高效地并行化，因为每个值都依赖于它前面的值。所以，无论你如何试图对其实施并行化，它天生就是一个顺序化的计算。所以我换成了另一个问题--查找数组中的最大元素，这样就好了。在这个动画中，你可以很精确地看到如何使用一个随机的数列去解决这个问题(如图4所示)。</span><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/JAC/JCA_04.PNG" /><br /><br /><span style="font-size: 10pt;"><strong>Java Magazine</strong>：你都在哪里讲演过这些动画程序？</span><br /><span style="font-size: 10pt;"><strong>Grazi</strong>：在JavaOne中讲演过几次，在其它的许多会议，如奥斯陆中的JavaZone，苏黎世的Jazoon，纽约的QCon，以及许多SIG(特别兴趣组)和JUG(Java用户组)中也都讲演过。我喜欢讲演，我也喜欢周游世界，而Java Concurrent Animated为我提供了一个极好的机会去做这两件事情。它总能获得极高的评价。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java Concurrent Animated的讲演提供了一种意识，并且它们也向出席的开发者们展示了下载这一框架的价值，而且它还展示了，如果你拥有了框架和灵感启迪，学习并发编程会是多么的容易。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/407310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-12-07 17:45 <a href="http://www.blogjava.net/jiangshachina/archive/2013/12/07/407310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为何喜欢在ThoughtWorks工作(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/08/22/403156.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 22 Aug 2013 06:43:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/08/22/403156.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/403156.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/08/22/403156.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/403156.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/403156.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">为何喜欢在ThoughtWorks工作</span></strong></div><span style="font-size: 10pt;">本文是ThoughtWorks首席科学家Martin Fowler接受InformIT采访时的<a href="http://www.informit.com/articles/article.aspx?p=2121640">访谈录</a>，谈到了在ThoughtWorks工作的情况，对ThoughWorks感兴趣的朋友们可以看看。(2013.08.23最后更新)</span><br /><br /><span style="font-size: 10pt;">Martin Fowler分享了他关于ThoughtWorks的看法，这是一家关注可持续性以及经济和社会公正的软件开发公司。Martin谈到了他是如何开始了在ThoughtWorks的工作，他为什么会喜欢这家公司的文化，以及对新的软件开发者的建议。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>你是怎样得到在<a href="http://www.thoughtworks.com/">ThoughtWorks</a>的工作？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>围绕一个当时他们正从事的项目的域模型，他们让我做些咨询工作。我们进展的不错，最后我就自然而然地加入进来了。在此期间，他们大步地转向极限编程这一软件开发风格。约九个月后，他们向我提供了一份offer。由于他们是我最喜欢的客户，我就决定加入他们。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>你在那儿工作多长时间了？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>12年。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>ThoughtWorks与其它公司有何不同？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>根本上，这种不同要归结为人。他们善于雇佣既聪明又乐于合作的人。特别是，这里会更多地以正直的态度去关注人。我发现，相比于过去多年中共事过的大多数客户，我更加信任我的同事。还有对完成高质量工作并期望做到更好的真挚热情，对于像我这样的作者，这是极好的素材。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>对于在那儿工作，你最喜欢的是什么？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>ThoughtWorks有许多东西可以<a href="http://martinfowler.com/">去写</a>。只是困难之处在于，我要挑哪块儿去讲述呢。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>工作在ThoughtWorks，你最自豪的是什么？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>我最自豪的是，我们从一家几百人的美国公司成长为在世界范围内拥有两千人的公司，而且原封不动地保持了公司文化中的精髓。我不确定我在其中扮演了什么角色，但这里一直是我喜欢工作的地方，并且公司一直在宣传我，使大家能有兴趣与ThoughtWorks协作，我对此感到高兴。</span><br /><span style="font-size: 10pt;">尽管如此，我仍不能肯定这种情况是否要大大地归功于我。在与我本人更明确相关的工作中，我必须得说，我很高兴在过去十年中建立了<a href="http://martinfowler.com/">martinfowler.com</a>。为了关注今后在改进这个站点时所做的事情，该站点已成为一个丰富的资源和永恒的迷题。</span><br /><span style="font-size: 10pt;">在更需要协作的工作前沿，我确实很高兴地看到我的一些同事已经成为业内举足轻重的"大嘴"。我不认为我在这其中有很大的作用--我给予的任何帮助能够轻易地超过他们自身的努力--但这确实是我最想做的贡献。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>为什么会有人想着去ThoughtWorks工作？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>对于经验少的人，我想最大的吸引力是，能在许多不同类型的项目中学会做好软件开发的能力。当然，ThoughtWorks的项目并非完美，但我认为它们比绝大多数软件项目要好得多。我听过很多ThoughtWorks前员工们谈到他们在公司工作的岁月中对于软件开发学到了许多。</span><br /><span style="font-size: 10pt;">而在所有的原因当中，旅行机会也是很重要的。如果你想花上一大段时间在世界上不同的地区进行工作，例如在美国人在印度工作，或巴西人在中国工作，那么ThoughtWorks提供了大量的此类机会。这也与我们日益关心社会正义的问题有关，我认为对于许多有经验的人来说这也是一个重要的因素。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>对于一个刚刚开始在ThoughtWorks工作的新员工，你有什么建议吗？</span><br /><span style="font-size: 10pt;"><strong>Martin Fowler：</strong>对于在ThoughtWorks工作的人们，最沮丧的事情之一就是我们不做职业规划，这就会很容易危险地在项目间漂来漂去。对于有些人来说这不是问题，但如果你想设定一个职业方向，那么你必须得自己去做。这意味着大量的交际，寻找机遇，并积极推进。这不是一种直接的途径，但在我成为独立咨询师之前，没有这种促进我前行的职业计划，就会有相反的效果。</span><br /><br /><span style="font-size: 10pt;"><strong>InformIT：</strong>告诉我们一个"只会发生在ThoughtWorks"的故事。</span><br /><strong><span style="font-size: 10pt;">Martin Fowler：</span></strong><span style="font-size: 10pt;">我记得有次被拉去讨论一个和BigCo相关的很有前景的项目。这笔交易确实很大，在初始阶段就要耗费约50+人/年。但存在着一些对BigCo在道德记录方面的担忧，尤其是在发展中国家。在这一讨论中，纯粹的ThoughtWorks要义是，CFO慷慨激昂地反对接下这笔高利润额的工作，而所有的高级领导们则倾听着一位来自南方国家最近才受雇于ThoughtWorks的初级开发者，他描述了BigCo的行径是怎样损害着他的国家。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/403156.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-08-22 14:43 <a href="http://www.blogjava.net/jiangshachina/archive/2013/08/22/403156.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你不是全面的专家(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/06/30/401088.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 30 Jun 2013 13:02:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/06/30/401088.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/401088.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/06/30/401088.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/401088.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/401088.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong style="font-size: 14pt;">你不是全面的专家</strong><strong style="font-size: 14pt;"><br /></strong></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 对于软件开发，干的时间越长，就越感觉自己懂的知识少，似乎要学习的知识还有很多很多。最近恰在<a href="http://www.dzone.com/links/index.html">DZone</a>上看到一篇<a href="http://architects.dzone.com/articles/youre-not-expert-all-levels">小文</a>，作者谈到是否有必要成为全面的专家。道理其实很浅显，大家也都懂，我只是随手翻译一下罢了。(2013.06.30最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 编程不仅仅是写代码--而是要处理一大堆抽象。从0和1开始，到处理器，内存地址，底层的网络协议，数据结构，再上升到网络服务，组件框架和用户界面。这一过程，就是从乏味的底层抽象，到更有趣的可用且可维护的抽象。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我们被归为"高级开发者"。对于某些人来说，这就意味着我们在所有的抽象层面都是专家。如果不是这样，那么我们就不够好。我不倾向于赞同这种观点，而这并不只是为了证明我并不是在所有抽象层面上都是专家。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这基本上就是一种折衷--要么你专注于某些层面，然后变得对它们十分精通，要么，你这也学学，那也干干，然后对哪一样也不精通。接下来你会争辩到，在多数领域的某些方面你应该达到专家级。但随着时间的推移，这些专业知识将会变得冗余。如果我今天把应用设计与框架方面的知识丢到一边，而去钻研算法或TCP编程，那么由于技术更新，三年后我可能就不再精通之前的那些领域了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 存在抽象的原因之一就是防止你陷入底层细节的深渊。如果没有这些抽象，我们就不可能写出合格的软件。而我们可能需要做的就是专注于更高层，因为低层已经被处理好了。将底层编程当作某种深奥艺术可能只是为了显摆，但事实上它并非什么深奥艺术，只是有对底层编程的需求罢了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 当然，这就不是说通过学习JQuery，Rails和Spring就能使你变成最好，而不需要知道其它东西了。你要尽可能地熟悉你所专长层面之下和之上层面的工作原理。一般地，因为所有抽象都会"泄露"，所以你不能忽略应用程序所消耗的内存，当需要快速访问时，你最好使用Hashtable，TCP连接可能被防火墙阻隔，尽管你不需要编写析构函数，不需要实现Hashtable，或者了解TCP。但了解其它层面的工作原理并不会使你成为那方面的专家，其实你也不必成为那种专家。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp; 为什么我要写这些呢？因为很多公司(包括大公司)中的人们倾向于认为专家就应该在所有层面上都达到专家级，尤其是在底层方面。在面试时会问你很多底层的细节问题，好像你一生都在从事数据结构和网络编程方面的工作，你当然不是那样，但这并不意味着你不是一名专家。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 不做全方位的专家是一件很好的事情，只需要在若干层上达到专家级就行了。但你必须了解更低层和更高层领域的工作原理，这样当有需要时，你也可以成为那一层的专家。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/401088.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-06-30 21:02 <a href="http://www.blogjava.net/jiangshachina/archive/2013/06/30/401088.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构遗留应用：案例研究(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/03/03/396007.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 03 Mar 2013 14:44:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/03/03/396007.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/396007.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/03/03/396007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/396007.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/396007.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">重构遗留应用：案例研究</span></strong></div><span style="font-size: 10pt;">本文是<a href="http://www.infoq.com/">InfoQ</a>中的一篇关于遗留系统重构的<a href="http://www.infoq.com/articles/refactoring-legacy-applications">文章</a>，该文基于一个真实案例，讲述了如何在重构遗留系统时编写单元测试，以及单元测试又是如何确保了重构的正确性。(2013.03.03最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 遗留代码臭不可闻。每个优秀的开发者都想对这样的代码进行重构，但为了重构，理想情况下，应该有一组单元测试用例以防止程序退化。但为遗留程序编写单元测试绝非易事；遗留代码往往是一团乱麻。对遗留程序编写高效的单元，可能需要先对它进行重构；而为了重构它，你又需要单元测试去确保重构不会破坏任何功能。所以这是一个先有鸡，还是先有蛋的问题。通过对我曾经工作过的一个真实案例的分享，本文描述了一种对遗留程序进行安全重构的方法论。</span><br /><br /><strong><span style="font-size: 12pt;">问题陈述</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在本文中，我使用一个真实案例阐述了在测试与重构遗留系统时的一种实用的实践过程。本案例由Java编写，但这一做法应该也适用于其它语言。为了照顾无经验者，我对本案例的场景进行了修改，并且对其进行了简化以使其更易于理解。本文介绍的实践均有助于最近我对遗留系统的重构。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这不是一篇 关于单元测试与重构基本技能的文章。要对关于这方面的主题进行更多学习，你可以阅读：<a href="http://martinfowler.com/">Martin Fowler</a>的<a href="http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672">Refactoring: Improving the Design of Existing Code</a>，以及<a href="https://twitter.com/JoshuaKerievsky">Joshua Kerievsky</a>的<a href="http://www.amazon.com/Refactoring-Patterns-Joshua-Kerievsky/dp/0321213351">Refactoring to Patterns</a>。本文不会描述在现实项目中时常会遇到的复杂场景，但我希望本文能为处理这些复杂场景提供一些有用的方法。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在该案例研究中，我将使用的例子是一个虚构的资源管理系统，在该系统中，一个资源代表一个能被分配一些任务的人。可以认为，一个资源能被分配一个Ticket--HR Ticket或IT Ticket。同时，一个资源也能被分配一个Request--HR Request或IT Request。资源经理可以记录资源要完成某项任务的评估时间。资源可以记录他或她在完全这个Ticket和Request时所耗费的实际时间。</span><br /><img alt="" src="http://www.infoq.com/resource/articles/refactoring-legacy-applications/en/resources/RefactoringLegacy01.png" height="320" width="471" /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 柱状图中展示了资源的使用情况，包括评估时间与实际时间。</span><br /><span style="font-size: 10pt;"></span><img alt="" src="http://www.infoq.com/resource/articles/refactoring-legacy-applications/en/resources/RefactoringLegacy02.png" /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 不复杂吧？然而，在真实系统中，资源会被分配到许多不同类型的任务。即使如此，从技术上看，这个设计并不复杂。然而，当第一次读到这些代码，我感觉我好像是在看一块化石。我能够看到这些代码是如何进化的(或者更应该说是退化)。最开始时，该系统只能处理Request，然后加入了处理Ticket和其它类型任务的功能。然后来一个开发工程师，他编写了处理Request的代码：从数据库中解析数据，将收集到的数据展示到柱状图中。他甚至都没有将数据信息置于一个合适的对象中：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;ResourceBreakdownService&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Map&nbsp;search&nbsp;(Session&nbsp;context)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;SearchException{&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">omitted&nbsp;twenty&nbsp;or&nbsp;so&nbsp;lines&nbsp;of&nbsp;code&nbsp;to&nbsp;pull&nbsp;search&nbsp;criteria&nbsp;out&nbsp;of&nbsp;context&nbsp;and&nbsp;verify&nbsp;them,&nbsp;such&nbsp;as&nbsp;the&nbsp;below:</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(resourceIds</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;resourceIds.size&nbsp;()</span><span style="color: #000000; ">==</span><span style="color: #000000; ">0</span><span style="color: #000000; ">){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SearchException(&#8220;Resource&nbsp;list&nbsp;is&nbsp;not&nbsp;provided&#8221;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(resourceId</span><span style="color: #000000; ">!=</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;resourceIds.size()</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resourceObjs</span><span style="color: #000000; ">=</span><span style="color: #000000; ">resourceDAO.getResourceByIds(resourceIds);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">get&nbsp;workload&nbsp;for&nbsp;all&nbsp;requests</span><span style="color: #008000; "><br /></span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Map&nbsp;requestBreakDown</span><span style="color: #000000; ">=</span><span style="color: #000000; ">getResourceRequestsLoadBreakdown&nbsp;(resourceObjs,startDate,finishDate);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;requestBreakDown;<br />&nbsp;&nbsp;&nbsp;}<br />}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我能肯定你会立即被这段代码的恶臭逼退。例如，你可能会马上想到<strong>search</strong>不是一个有意义的名字，应该使用Apache Commons类库的<strong>CollectionUtil.isEmpty()</strong>去测试这个集合对象，你可能也会对返回结果<strong>Map</strong>提出质疑。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 但先等等，臭味还在继续集聚中。又来了一个开发工程师，并使用相同的方法来处理Ticket，所以你会看到这样的代码：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;get&nbsp;workload&nbsp;for&nbsp;all&nbsp;tickets</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">Map&nbsp;ticketBreakdown&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">getResourceRequestsLoadBreakdown(resourceObjs,startDate,finishDate,ticketSeverity);<br />Map&nbsp;result</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;HashMap();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(Iterator&nbsp;i&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;resourceObjs.iterator();&nbsp;i.hasNext();)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;Resource&nbsp;resource</span><span style="color: #000000; ">=</span><span style="color: #000000; ">(Resource)i.next();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Map&nbsp;requestBreakdown2</span><span style="color: #000000; ">=</span><span style="color: #000000; ">(Map)requestBreakdown.get(resource);<br />&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;ticketBreakdown2</span><span style="color: #000000; ">=</span><span style="color: #000000; ">(List)ticketBreakdown.get(resource);<br />&nbsp;&nbsp;&nbsp;&nbsp;Map&nbsp;resourceWorkloadBreakdown</span><span style="color: #000000; ">=</span><span style="color: #000000; ">combineRequestAndTicket(requestBreakdown2,&nbsp;ticketBreakdown2);<br />&nbsp;&nbsp;&nbsp;&nbsp;result.put(resource,resourceWorkloadBreakdown)<br />}<br /></span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;result;</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 先不必考虑命名，结构的平衡性，以及其它美学方面的关切，最臭不可闻的就是返回的Map对象。<strong>Map</strong>是一个黑洞，它可以吸入任何对象，但不会为你提供足够的线索去弄清楚它所包含的确切对象。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在这个例子中，<strong>{}</strong>表示一个Map对象，<strong>=&gt;</strong>代表一个键-值对象，而<strong>[]</strong>代表一个集合对象：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">{resource&nbsp;with&nbsp;id&nbsp;</span><span style="color: #000000; ">30000</span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">&nbsp;[<br />&nbsp;&nbsp;&nbsp;&nbsp;SummaryOfActualWorkloadForRequestType,<br />&nbsp;&nbsp;&nbsp;&nbsp;SummaryOfEstimatedWorkloadForRequestType,<br />&nbsp;&nbsp;&nbsp;&nbsp;{</span><span style="color: #000000; ">30240</span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">[<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActualWorkloadForReqeustWithId_30240,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EstimatedWorkloadForRequestWithId_30240],&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;</span><span style="color: #000000; ">30241</span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">[<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActualWorkloadForReqeustWithId_30241,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EstimatedWorkloadForRequestWithId_30241]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SummaryOfActualWorkloadForTicketType,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SummaryOfEstimatedWorkloadForTicketType,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span><span style="color: #000000; ">20000</span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">[<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActualWorkloadForTicketWithId_2000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EstimatedWorkloadForTicketWithId_2000],<br />&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;]<br />}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用如此糟糕的数据结构，数据的组装与分解逻辑确实不易读懂，而且极其冗长。</span><br /><br /><strong><span style="font-size: 12pt;">集成测试</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 希望到目前为止，我已经让你相信这些代码确实很复杂。如果在开始重构之前，我要首先解开这一团乱麻，还得理解每一处代码，那我可能会发疯。为了保持头脑清醒，我决定去理解程序的逻辑，最好是使用由上至下的途径去进行理解。即，不去阅读代码，也不去推演程序逻辑，而最好是去使用该系统并对其进行调试，以便在总体上对其进行理解。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在编写单元测试时，我也使用相同的方法。传统观点是编写小的单元测试去验证每一块程序，如果每个测试都能正常通过，那么当你把所有的程序汇集到一块儿时，有很大的可能性，整个应用都能够正常运行。但该方法不适用此处。<strong>ResourceBreakdownService</strong>是一个"<a href="http://c2.com/cgi/wiki?GodClass">上帝类</a>"。如果在只是大体认知的情况下，我一开始就想着去分割这个类，那么我会范下很多错误--遗留系统的每个犄角旮旯中都可能隐藏着许多秘密。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我编写了一个简单的单元测试，它反映了我对应用整体结构的理解：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;testResourceBreakdown(){<br />&nbsp;&nbsp;&nbsp;&nbsp;Resource&nbsp;resource</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createResource();<br />&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;requests</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createRequests();<br />&nbsp;&nbsp;&nbsp;&nbsp;assignRequestToResource(resource,&nbsp;requests);<br />&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;tickets</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createTickets();<br />&nbsp;&nbsp;&nbsp;&nbsp;assignTicketToResource(resource,&nbsp;tickets);<br />&nbsp;&nbsp;&nbsp;&nbsp;Map&nbsp;result</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ResourceBreakdownService().search(resource);<br />&nbsp;&nbsp;&nbsp;&nbsp;verifyResult(result,resource,requests,tickets);<br />}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 请注意<strong>verifyResult()</strong>方法。首先，我通过递归地打印返回结果的内容来了解它的结构。<strong>verifyResult()</strong>方法使用这一结构去验证返回值是否包含有正确的数据：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;verifyResult(Map&nbsp;result,&nbsp;Resource&nbsp;rsc,&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Request</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;requests,&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ticket</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;tickets){&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;assertTrue(result.containsKey(rsc.getId()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;in&nbsp;this&nbsp;simple&nbsp;test&nbsp;case,&nbsp;actual&nbsp;workload&nbsp;is&nbsp;empty</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;UtilizationBean&nbsp;emptyActualLoad</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createDummyWorkload();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;resourceWorkLoad</span><span style="color: #000000; ">=</span><span style="color: #000000; ">result.get(rsc.getId());&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;UtilizationBean&nbsp;scheduleWorkload</span><span style="color: #000000; ">=</span><span style="color: #000000; ">calculateWorkload(rsc,requests);<br />&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(emptyActualLoad,resourceWorkLoad.get(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(scheduleWorkload,resourceWorkLoad.get(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&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;Map&nbsp;requestDetailWorkload&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(Map)resourceWorkLoad.get(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(Request&nbsp;request&nbsp;:&nbsp;requests)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue(requestDetailWorkload.containsKey(request.getId());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UtilizationBean&nbsp;scheduleWorkload0</span><span style="color: #000000; ">=</span><span style="color: #000000; ">calculateWorkload(rsc,request);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(emptyActualLoad,requestDetailWorkload.get(request.getId()).get(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(scheduleWorkload0,requestDetailWorkload.get(request.getId()).get(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;omit&nbsp;code&nbsp;to&nbsp;check&nbsp;tickets</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</span></div><br /><strong><span style="font-size: 12pt;">绕开障碍</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 上述单元测试开起来简单，但实际上复杂。首先，<strong>ResourceBreakdownService.search()</strong>方法与运行时环境紧密绑定，它需要访问数据库，其它服务，鬼知道还需要些什么。像许多遗留系统一样，该资源管理系统没有任何单元测试基础架构。为了访问运行时服务，只能开启整个系统，这不仅昂贵，而也不方便。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 启动系统服务器端的类是ServerMain。该类也像一块化石，通过它你能看到程序的演进过程。该系统是10年前写成的，那时还没有Spring和Hibernate，只有JBoss和Tomcat的早期版本。在那时，勇敢的先行者们不得不新手动手做很多事情，所以他们创建了自用的集群，缓存服务，以及连接池。后来，他们以某种方式加入了JBoss和Tomcat(但不幸地是，他们留下了自己创建的服务，所以程序遗有了两套事务管理机制和三种连接池。) </span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 我决定将<strong>ServerMain</strong>复制到<strong>TestServerMain</strong>中，当调用<strong>TestServerMain.main()</strong>时却失败了：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">org.springframework.beans.factory.BeanInitializationException:&nbsp;Could&nbsp;not&nbsp;load&nbsp;properties;&nbsp;nested&nbsp;exception&nbsp;is<br />java.io.FileNotFoundException:&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;path&nbsp;resource&nbsp;[database.properties]&nbsp;cannot&nbsp;be&nbsp;opened&nbsp;because&nbsp;it&nbsp;does&nbsp;not&nbsp;exist<br />at<br />org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:</span><span style="color: #000000; ">78</span><span style="color: #000000; ">)</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 是的，很容易就能搞定！我弄来一个<strong>database.properties</strong>属性文件，并把它置入测试类路径中，再次启动测试程序。这次则抛出一个异常：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">java.io.FileNotFoundException:&nbsp;.\server.conf&nbsp;(The&nbsp;system&nbsp;cannot&nbsp;find&nbsp;the&nbsp;file&nbsp;specified)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.io.FileInputStream.open(Native&nbsp;Method)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.io.FileInputStream.</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">init</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(FileInputStream.java:</span><span style="color: #000000; ">106</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.io.FileInputStream.</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">init</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(FileInputStream.java:</span><span style="color: #000000; ">66</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.io.FileReader.</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">init</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(FileReader.java:</span><span style="color: #000000; ">41</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.config.ServerConfigAgent.parseFile(ServerConfigAgent.java:</span><span style="color: #000000; ">1593</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.config.ServerConfigAgent.parseConfigFile(ServerConfigAgent.java:</span><span style="color: #000000; ">1720</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.config.ServerConfigAgent.parseConfigFile(ServerConfigAgent.java:</span><span style="color: #000000; ">1712</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.config.ServerConfigAgent.readServerConf(ServerConfigAgent.java:</span><span style="color: #000000; ">1581</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.ServerConfigFactory.initServerConfig(ServerConfigFactory.java:</span><span style="color: #000000; ">38</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.util.HibernateUtil.setupDatabaseProperties(HibernateUtil.java:</span><span style="color: #000000; ">207</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.util.HibernateUtil.doStart(HibernateUtil.java:</span><span style="color: #000000; ">135</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.foo.bar.util.HibernateUtil.</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">clinit</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(HibernateUtil.java:</span><span style="color: #000000; ">125</span><span style="color: #000000; ">)</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; server.conf是存在于某处，但这种方法让我很不爽。只不过是写个单元测试，就马上暴露出了程序中的问题。<strong>HibernateUtil</strong>，顾名思义，应该只关注由<strong>database.properties</strong>文件提供的数据库信息。那为什么它还需要访问<strong>server.conf</strong>文件呢？该文件是用来配置服务器端参数的。有一线索可用来判断代码是否有异味：如果你感觉你是在读一部侦探小说，并不停在问"为什么"，那么这段代码通常就不好。我已经花了大量时间去阅读<strong>ServerConfigFactory</strong>，<strong>HibernateUtil</strong>和<strong>ServerConfigAgent</strong>中的代码，并且还尝试着去弄清楚<strong>HibernateUtil</strong>是如何使用<strong>database.properties</strong>，但在这个问题上，使我急切地想得到一个运行的服务器。除了这种方法以外，还有一种解决途径，这个武器就是<a href="http://eclipse.org/aspectj/">AspectJ</a>。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;around():<br />&nbsp;&nbsp;&nbsp;&nbsp;call(</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;com.foo.bar.ServerConfigFactory.initServerConfig()){<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">bypassing&nbsp;com.foo.bar.ServerConfigFactory.initServerConfig</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 对不懂AspectJ的伙计们来说，上面那段话的意思是：当运行到对<strong>ServerConfigFactory.initServerConfig()</strong>的调用时，AspectJ会打印一条信息，然后直接返回，而不再进入该方法本身。感觉这就像是骇客入侵，但这非常高效。遗留系统中满是谜团与问题。在任一给定的时刻，每个人都需要作出他具有战略性的一击。当时，依据用户满意度，我做的最值得称道的事情就是修复了资源管理系统中的缺陷，并改进了它的性能。矫正其它方面的问题并不是我需要关注的。但是，我会把它记在头里，之后我会转回来解决<strong>ServerMain</strong>中的乱麻。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在<strong>HibernateUtil</strong>从<strong>server.conf</strong>中读取必要信息这个调用点，我指示它从<strong>database.properties</strong>中读取这些信息：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">String&nbsp;around():call(</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;com.foo.bar.config.ServerConfig.getJDBCUrl()){<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;code&nbsp;omitted,&nbsp;reading&nbsp;from&nbsp;database.properties</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">}<br /><br />&nbsp;&nbsp;&nbsp;String&nbsp;around():call(</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;com.foo.bar.config.ServerConfig.getDBUser()){<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;code&nbsp;omitted,&nbsp;reading&nbsp;from&nbsp;database.properties</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 你可能已经猜到剩下的工作了：解决运行时障碍，使它做起来更方便或更自然些。但如果已经现成的Mock对象，那就复用它。例如，<strong>TestServerMain.main()</strong>会在如下位置失败：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;Factory&nbsp;name:&nbsp;java:comp</span><span style="color: #000000; ">/</span><span style="color: #000000; ">env</span><span style="color: #000000; ">/</span><span style="color: #000000; ">hibernate</span><span style="color: #000000; ">/</span><span style="color: #000000; ">SessionFactory<br /></span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;JNDI&nbsp;InitialContext&nbsp;properties:{}<br /></span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;Could&nbsp;not&nbsp;bind&nbsp;factory&nbsp;to&nbsp;JNDI<br />javax.naming.NoInitialContextException:&nbsp;Need&nbsp;to&nbsp;specify&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;name&nbsp;in&nbsp;environment&nbsp;or&nbsp;system&nbsp;property,&nbsp;or&nbsp;as&nbsp;an&nbsp;applet<br />parameter,&nbsp;or&nbsp;in&nbsp;an&nbsp;application&nbsp;resource&nbsp;file:&nbsp;java.naming.factory.initial<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:</span><span style="color: #000000; ">645</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:</span><span style="color: #000000; ">288</span><span style="color: #000000; ">)</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这是因为没有启动JBoss的命名服务。我可以使用相同的AspectJ技术去解决这个问题，但InitialContext是一个很大的Java接口，它包含有许多方法，我不想去实现每个方法--那实在太繁冗了。随便查了一下，发现Spring已经提供一个Mock类<strong>SimpleNamingContext</strong>，所以就在测试用例使用这个类：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">SimpleNamingContextBuilder&nbsp;builder&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleNamingContextBuilder();<br />builder.bind(&#8220;java:comp</span><span style="color: #000000; ">/</span><span style="color: #000000; ">env</span><span style="color: #000000; ">/</span><span style="color: #000000; ">hibernate</span><span style="color: #000000; ">/</span><span style="color: #000000; ">SessionFactory&#8221;,sessionFactory);<br />builder.activate();</span></div><br /><div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 经过几轮尝试后，我已能成功执行<strong>TestServerMain.main()</strong>方法了。与<strong>ServerMain</strong>相比，它要简单多了，它模拟了许多JBoss服务，并且它没有纠结于集群管理。</span></div><br /><strong><span style="font-size: 12pt;">创建构建模块</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>TestServerMain</strong>连接到一个真实的数据库。遗留系统会将意想不到的逻辑隐藏于存储过程中，更糟糕地是，甚至会隐藏于触发器中。基于相同的整体结构考虑，我认为当时试图去理解隐藏于数据库中的神秘逻辑并去模拟这样的一个数据库是不明智的，所以我决定让测试用例去访问真实的数据库。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 需要重复地执行测试用例，以确保我对产品做的每一点儿改变都能够通过测试。在每次执行时，测试程序将在数据库中创建资源与请求。不同于单元测试的传统套路，有时候你并不想在每个用例执行完成之后销毁由该用例创建的数据，以使运行环境变得干净。到目前为止，测试与重构实践是一种实情调查的探索方式--通过尝试着进行测试去学习遗留系统。你可能想检查由测试用例在数据库中创建的数据，或者为了确定每一项功能都能如期运行，你可能还想在运行时系统中使用这些数据。这就意味着，测试用例必须在数据库中创建独一无二的数据实例，以避免与其它用例冲突。应该有一些工具类去方便地创建这些数据实例。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 此处有一个简单的创建<strong>资源</strong>的构建模块：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;ResourceBuilder&nbsp;newResource&nbsp;(String&nbsp;userName)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;ResourceBuilder&nbsp;rb&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ResourceBuilder();<br />&nbsp;&nbsp;&nbsp;&nbsp;rb.userName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;userName&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;UnitTestThreadContext.getUniqueSuffix();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;rb;&nbsp;}<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;ResourceBuilder&nbsp;assignRole(String&nbsp;roleName)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.roleName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;roleName&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;UnitTestThreadContext.getUniqueSuffix();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />}<br />&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Resource&nbsp;create()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;ResourceDAO&nbsp;resourceDAO&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ResourceDAO(UnitTestThreadContext.getSession());<br />&nbsp;&nbsp;&nbsp;&nbsp;Resource&nbsp;rs;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(StringUtils.isNotBlank(userName))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;resourceDAO.createResource(</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.userName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RuntimeException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">must&nbsp;have&nbsp;a&nbsp;user&nbsp;name&nbsp;to&nbsp;create&nbsp;a&nbsp;resource</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(StringUtils.isNotBlank(roleName))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Role&nbsp;role&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;RoleBuilder.newRole(roleName).create();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs.addRole(role);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;rs;<br />&nbsp;}<br />&nbsp;<br />&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;delete(Resource&nbsp;rs,&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;cascadeToRole)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Session&nbsp;session&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;UnitTestThreadContext.getSession();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResourceDAO&nbsp;resourceDAO&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ResourceDAO(session);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resourceDAO.delete(rs);<br />&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(cascadeToRole)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RoleDAO&nbsp;roleDAO&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RoleDAO(session);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;roles&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;rs.getRoles();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(Object&nbsp;role&nbsp;:&nbsp;roles)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;roleDAO.delete((Role)role);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <strong>ResourceBuilder</strong>是Builder与Factory模式的实现；你得悠着点儿去用它：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">ResourceBuilder.newResource(&#8220;Tom&#8221;).assignRole(&#8220;Developer&#8221;).create();</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 该类也包含一个战场清扫方法：<strong>delete()</strong>。在早期的重构实践中，我没有经常调用这个方法，因为我经常启动该系统，然后引入测试用例中的数据，并检验柱状图是否正确。</span><br /><span style="font-size: 10pt;">此处非常有用的一个类就是<strong>UnitTestThreadContext</strong>，它按照线程的不同去存储Hibernate会话，并为每个你想创建的数据实体的名称的后面加上一个唯一的字符串，因此保证了实体的唯一性。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;UnitTestThreadContext&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;ThreadLocal</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Session</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;threadSession</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ThreadLocal</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Session</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;ThreadLocal</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;threadUniqueId</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ThreadLocal</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;SimpleDateFormat&nbsp;dateFormat&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy/MM/dd&nbsp;HH_mm_ss_S</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Session&nbsp;getSession(){</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Session&nbsp;session&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;threadSession.get();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(session</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RuntimeException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Hibernate&nbsp;Session&nbsp;not&nbsp;set!</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;session;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setSession(Session&nbsp;session)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadSession.set(session);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;String&nbsp;getUniqueSuffix()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;uniqueId&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;threadUniqueId.get();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(uniqueId</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uniqueId&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">-</span><span style="color: #000000; ">"</span><span style="color: #000000; ">+</span><span style="color: #000000; ">dateFormat.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadUniqueId.set(uniqueId);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;uniqueId;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br />&nbsp;}</span></div><br /><strong><span style="font-size: 12pt;">集中起来看</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 现在我可以启动这个最低限度的基础框架了，并能运行简单的测试用例：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setUp()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestServerMain.run();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">setup&nbsp;a&nbsp;minimum&nbsp;running&nbsp;infrastructure</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;testResourceBreakdown(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Resource&nbsp;resource</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createResource();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">use&nbsp;ResourceBuilder&nbsp;to&nbsp;build&nbsp;unique&nbsp;resources</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;requests</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createRequests();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">use&nbsp;RequestBuilder&nbsp;to&nbsp;build&nbsp;unique&nbsp;requests</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assignRequestToResource(resource,&nbsp;requests);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;tickets</span><span style="color: #000000; ">=</span><span style="color: #000000; ">createTickets();&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">use&nbsp;TicketBuilder&nbsp;to&nbsp;build&nbsp;unique&nbsp;tickets</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assignTicketToResource(resource,&nbsp;tickets);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Map&nbsp;result</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ResourceBreakdownService().search(resource);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;verifyResult(result);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;tearDown()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;use&nbsp;TicketBuilder.delete()&nbsp;to&nbsp;delete&nbsp;tickets<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;use&nbsp;RequestBuilder.delete()&nbsp;to&nbsp;delete&nbsp;requests<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;use&nbsp;ResourceBuilder.delete()&nbsp;to&nbsp;delete&nbsp;resources</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用该方法，我继续编写更复杂的测试用例，重构产品程序与测试程序。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 靠着这些测试用例的武装，我对"上帝类"<strong>ResourceBreakdownService</strong>一点点儿地进行分解。我不会透露更多的细节，这会使你难以理解；有许多书都可以教你如何安全地进行重构。</span><br /><img alt="" src="http://www.infoq.com/resource/articles/refactoring-legacy-applications/en/resources/RefactoringLegacy03.png" /><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 糟糕的<strong>Map_Of_Array_Of_Map_Of&#8230;</strong>数据结构现在被组装成<strong>ResourceLoadBucket</strong>，该类使用了<a href="http://en.wikipedia.org/wiki/Composite_pattern">Composite模式</a>。它包含有特定级别的评估工效与实际工效，下一级别的工效可通过<strong>aggregate()</strong>方法聚合而成。最终程序要清洁的多，性能也更优。它甚至暴露了一些隐藏于原有代码复杂逻辑中的缺陷。当然，我也根据这种方法改进了单元测试。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 贯穿于这次重构实践，我始终坚持的关键原则就是大局观思想。我挑选着工作方向并持续坚持着大局观原则，绕开当前并不重要的任务，然后构建一个最低限度的测试基础架构，我的团队则会继续使用这个基础架构去重构系统的其它部分。仍然在测试基础架构中保留了一些骇客侵入式的AspectJ程序，因为在业务上没有必要去清除它们。我不仅重构了一个非常复杂的功能领域，而且还对遗留系统进行了深入的认知。对待一个遗留应用，就如同对待一件易碎的瓷器，它不会给你带来任何安全感。深入其中，并对其进行重构，如此，遗留应用才可能在未来得以幸存。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/396007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-03-03 22:44 <a href="http://www.blogjava.net/jiangshachina/archive/2013/03/03/396007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java.next: 下一代JVM语言(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 05 Feb 2013 16:04:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/395164.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/395164.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/395164.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">Java.next: 下一代JVM语言</span></strong></div><span style="font-size: 10pt;">本文是ThoughtWorks公司架构师Neal Ford在IBM <a href="http://www.ibm.com/developerworks/">developerWorks</a>系列文章java.next中的<a href="http://www.ibm.com/developerworks/library/j-jn1/index.html">第一篇</a>，其基于Groovy，Scala和Clojure，讲述了多语言编程的重要性，并对静态类型与动态类型，函数式编程与命令式编程进行了比较。(2013.02.06最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在我与Martin Fowler曾经合作呈现的一次主题演讲中，他作出了一个有洞察性的观点：</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; <em>Java的遗产将是平台，而不是程序设计语言。</em></span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java技术的原始工程师们作出了一个明智的决定，就是将编程语言与运行时环境分开，最终这使得超过200种语言能够运行在Java平台上。这种架构对于该平台的长期活力是至关重要的，因为计算机程序设计语言的寿命一般都是比较短。从2008年开始，由Oracle主办的一年一度的JVM语言峰会为JVM上其它的语言实现与Java平台工程师进行开放式合作提供了机遇。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 欢迎来到<a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=java+next:">Java.next</a>专栏系列，在本系列的文章中，我将讲述三种现代JVM语言--Groovy，Scala和Clojure--它提供了范式，设计选择与舒适因子之间一种有趣的混合。在此我不会花时间去深入介绍每种语言；在它们各自的站点上都有这类深度介绍。但这些语言社区的站点--它们主要目的是为了传布这些语言--都缺乏客观的信息，或者是该语言不适用的例子。在本系列的文章中我将进行独立地比较，以填补上述空白。这些文章将概述Java.next程序设计语言，以及学习它们的好处。</span><br /><br /><strong><span style="font-size: 12pt;">超越Java</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java程序设计语言达到卓越的程度就是，按Bruce Tate在他的Beyond Java一书中的说法，完美风暴：Web应用的兴起，已有Web技术由于种种原因不能适应需求，企业级多层应用开发的兴起，这些因素共同造就了Java的卓越。Tate也指出这场风暴是一系列独一无二的事件，曾经没有其它语言使用相同的途径达到相同的卓越程序。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java语言已经证明其在功能方面的强大灵活性，但它的语法与固有范式则存在着长期已知的局限性。尽管一些承诺过的变化即将引入到该语言中，但Java语法却不能很容易地支持一些重要的未来语法特性，例如函数式编程中的某些特性。但如果你试图去找到一种语言去替代Java，那么你就找错了。</span><br /><br /><strong><span style="font-size: 10pt;">多语言编程</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 多语言编程--在2006年的一篇博客中我使这个术语重焕活力并重新流行起来--是基于这样的一种认识：没有一种编程语言能够解决每个问题。有些语言拥有某些内建的特性，使其能够更好地适应特定的问题。例如，由于Swing十分复杂，开发者们发现很难编写Java中的Swing UI，因为它要求事先声明类型，为UI动作定义烦人的匿名内部类，还有其它的麻烦事儿。使用更适合构建UI的语言，如Groovy中的SwingBuilder工具，去构建Swing应用会美妙得多。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 运行在JVM上的程序设计语言大量增多，这大大激发了多语言编程理念，因为你可以混用编号语言，并可使用最佳匹配的语言，但同时却维护着相同的底层字节码和类库。例如，SwingBuilder并不是要替代Swing；它只是搭建在已有的Swing API之上。当然，在相当长的时间内，开发者们还是将在JVM之外混合使用编程语言--例如，为特定目的而使用SQL和JavaScript--但在JVM的世界内，混合编程将变得更为流行。ThoughtWorks中的许多项目就合用着多种编程语言，而所有由ThoughtWorks Studios开发的工具则都要使用混合语言。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 即便Java仍是你主要的开发语言，学习一下其它语言是如何工作的会让你将它们纳入你的未来战略中。Java仍将是JVM生态系统中的重要组成部分，但最终它更多是作为该平台的汇编语言--或是由于纯粹的性能原因，或是在应对特殊需求时才会用到它。</span><br /><br /><strong><span style="font-size: 10pt;">编程语言的进化</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 当上世纪八十年代我还在大学时，我们使用着一种称作Pecan Pascal的开发环境。它独一无二的特性就是能使相同的Pascal代码既可运行在Apple II上，又可以运行在IBM PC上。Pecan的工程师们为了实现这一目的使用了一种称作"字节码"的神秘之物。开发者们将他们的Pascal代码编译成"字节码"，该"字节码"则运行在为各个平台编写的原生"虚拟机"上。那是一段可怕的经历！最终程序慢的出奇，即便只是一个简单的类赋值。当时的硬件无法应对这一挑战。</span><br /><span style="font-size: 10pt;">Pecan Pascal之后的十年，Sun发布了使用相同架构的Java，它受限也受利于上世纪九十年代的硬件环境。Java还加入了其它的对开发者友好的特性，如自动的垃圾收集。由于曾经使用过像C++之样的语言，现在我再也不想使用没有垃圾收集功能的语言去编码了。我宁愿花时间在更高抽象层次上去思考复杂的业务问题，而不是像内存管理这样的复杂管道问题。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 计算机语言通常没有很长寿命的原因之一就是语言和平台设计的创新速度。由于我们的平台变得更为强大，它们可以处理更多的额外工作。例如，Groovy的内存化(memoization)特性(2010年加入)会缓存函数调用的结果。不需要手工编写缓存代码，那样会引入潜在的缺陷，你仅仅只是需要调用memoize方法而以，如清单1所示：</span><br /><strong><span style="font-size: 10pt;">清单1. 在Groovy中内存化函数</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-size: 10pt;">def&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">static</span><span style="color: #000000; font-size: 10pt;">&nbsp;sum&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;{&nbsp;number&nbsp;</span><span style="color: #000000; font-size: 10pt;">-&gt;</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;factorsOf(number).inject(</span><span style="color: #000000; font-size: 10pt;">0</span><span style="color: #000000; font-size: 10pt;">,&nbsp;{i,&nbsp;j&nbsp;</span><span style="color: #000000; font-size: 10pt;">-&gt;</span><span style="color: #000000; font-size: 10pt;">&nbsp;i&nbsp;</span><span style="color: #000000; font-size: 10pt;">+</span><span style="color: #000000; font-size: 10pt;">&nbsp;j})<br /></span><span style="color: #000000; font-size: 10pt;">}<br /></span><span style="color: #000000; font-size: 10pt;">def&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">static</span><span style="color: #000000; font-size: 10pt;">&nbsp;sumOfFactors&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;sum.memoize()</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单1中，sumOfFactors方法返回的结果会被自动缓存。你还可以使用方法memoizeAtLeast()和memoizeAtMost()去定制缓存行为。Clojure也含有内存化特性，在Scala中也有略有实现。像内存化这样存在于下一代编程语言(以及某些Java框架)中的高级特性也将逐渐地进入到Java语言中。Java的下一个版本中将加入高阶函数(higher-order function)，这使得内存化更容易被实现。通过研究下一代Java语言，你就可以先睹Java的未来特性为快了。</span><br /><br /><strong><span style="font-size: 12pt;">Groovy，Scala和Clojure</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Groovy是二十一世纪的Java语法--浓缩咖啡取代了传统咖啡。Groovy的设计目标是更新并消除Java语法中的障碍，同时还要支持Java语言中的主要编程范式。因此，Groovy要"知晓"诸如JavaBean，它会简化对属性的访问。Groovy会以很快的速度纳入新特性，包括函数式编程中的重要特性，这些特性我将在本系列的后续篇章中着重描述。Groovy仍然主要是面向对象的命令式语言。Groovy区别于Java的两个基本不同点：它是动态而非静态的；它是的元编程能力要好得多。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Scala从骨子里就是为了利用JVM而进行设计的，但是它的语法则是完全被重新设计过了。Scala是强静态类型语言--它的类型要求比Java还严格，但造成的麻烦却很少--它支持面向对象和函数式范式，但更偏好于后者。例如，Scala更喜欢val声明，这会生成不可变变量(类似于在Java中将变量声明为final)赋给var，而var将创建更为大家所熟悉的可变变量。通过对这两种范式的深度支持，Scala为你可能想要的(面向对象的命令式编程)与你所应该想要的(函数式编程)之间架起了一座桥梁。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Clojure是最激进的，它的语法是从其它语言中分离出来，被认为是Lisp的方言。Clojure是强动态类型语言(就像Groovy)，它反映了一种义无反顾的设计决策。虽然Clojure允许你与遗留的Java程序进行全面而深度的交互，但是它并不试图构建一座桥梁去连接面向对象范式。例如，Clojure是函数式编程的铁杆，也支持面向对象以允许与该种范式进行互操作。尽管它支持面对对象程序员所习惯的全部特性，如多态--但，是以函数式风格，而非面向对象风格进行实现的。设计Clojure时遵循了一组核心的工程原则，如软件事务内存(Software Transactional Memory)，这是为了迎合新功能而打破了旧有的编程范式。</span><br /><br /><strong><span style="font-size: 12pt;">编程范式</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 除语法之外，这些语言之间的最有趣的不同之处就是类型及其内在的编程范式：函数式或命令式。</span><br /><br /><strong><span style="font-size: 10pt;">静态类型 vs. 动态类型</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 编程语言中的静态类型要求显式的类型声明，例如Java中的int x;声明语句。动态类型语言并不要求在声明时提供类型信息。此处所考虑的语言都是强类型语言，意即程序在赋值之后能够反射出类型。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java的类型系统广受诟病之处就是其静态类型有太多不便，且又没有提供足够的益处。例如，在当前的有限的类型推导出现之前，Java要求开发者在赋值语句两边要重复地声明类型。Scala的类型比Java的更为静态，但在日常使用中所遇到的不便要少得多，因为它大量使用了类型推导。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 初看Groovy，它似乎有一种行为能够衔接静态与动态之间的隔阂。考虑如清单2所示的简单对象集合工厂：</span><br /><strong><span style="font-size: 10pt;">清单2. Groovy集合工厂</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-size: 10pt;">class</span><span style="color: #000000; font-size: 10pt;">&nbsp;CollectionFactory&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;def&nbsp;List&nbsp;getCollection(description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">if</span><span style="color: #000000; font-size: 10pt;">&nbsp;(description&nbsp;</span><span style="color: #000000; font-size: 10pt;">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Array-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;ArrayList()<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">else</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">if</span><span style="color: #000000; font-size: 10pt;">&nbsp;(description&nbsp;</span><span style="color: #000000; font-size: 10pt;">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;Stack()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 清单2中的类表现为一个工厂类，基于传入的description参数，该工厂返回List接口的两种实现--ArrayList或Stack--之一。对于Java开发者，上述代码确保了返回值能够符合约定。然后，清单3中的两个单元测试揭示了一种复杂性：</span><br /><strong><span style="font-size: 10pt;">清单3. Groovy中的集合类型测试</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-size: 10pt;">@Test<br /></span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;test_search()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;List&nbsp;l&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertTrue&nbsp;l&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">instanceof</span><span style="color: #000000; font-size: 10pt;">&nbsp;java.util.Stack<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;l.push(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertThat&nbsp;l.size(),&nbsp;is(</span><span style="color: #000000; font-size: 10pt;">1</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;def&nbsp;r&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;l.search(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">}<br /><br /></span><span style="color: #000000; font-size: 10pt;">@Test(expected</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">groovy.lang.MissingMethodException.</span><span style="color: #0000ff; font-size: 10pt;">class</span><span style="color: #000000; font-size: 10pt;">)&nbsp;<br /></span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;verify_that_typing_does_not_help()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;List&nbsp;l&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Array-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertTrue&nbsp;l&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">instanceof</span><span style="color: #000000; font-size: 10pt;">&nbsp;java.util.ArrayList<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;l.add(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;assertThat&nbsp;l.size(),&nbsp;is(</span><span style="color: #000000; font-size: 10pt;">1</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;def&nbsp;r&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;l.search(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单3中的第一个单元测试中，使用前述的工厂类获得一个Stack对象，并验证它是否确实是Stack对象，然后再执行栈操作，例如push()，size()和search()。然而，在第二个单元测试中，我必须声明一个期望的异常MissingMethodException才能确保该测试能够通过。当我获取一个Array-like的集合，并将它赋给List类型的变量时，我能够验证返回的类型确为一个List对象。但是，当我试图调用search()方法时将触发异常，因为ArrayList并不包含search()方法。因此，这种声明无法在编译时确保方法的调用是正确的。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 虽然这看起来像是一个缺陷，但这种行为却是恰当的。Groovy中的类型只是确保赋值语句的有效性。例如，在清单3中，如果返回的实例未实现List接口，将会触发一个运行时异常GroovyCastException。鉴于此，可以肯定Groovy能够与Clojure同跻身于强动态类型语言家族。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 然而，Groovy最新的一些变化使得它的静态与动态之间的隔阂变得扫地清。Groovy 2.0加入了注解@TypeChecked，该注解可使你特别地对类或方法决定进行严格的类型检查。清单4例证该注解的使用：</span><br /><strong><span style="font-size: 10pt;">清单4. 使用注解的类型检查</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-size: 10pt;">@TypeChecked<br /></span><span style="color: #000000; font-size: 10pt;">@Test&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;type_checking()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;f&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;CollectionFactory()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;l&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;l.add(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;r&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;l.pop()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;assertEquals&nbsp;r,&nbsp;</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单4中，我加入了注解@TypeChecked，它同时对赋值及随后的方法调用进行了验证。例如，清单5中的代码将不能通过编译： </span></div><strong><span style="font-size: 10pt;">清单5. 防止无效方法调用的类型检查</span></strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-size: 10pt;">@TypeChecked<br /></span><span style="color: #000000; font-size: 10pt;">@Test&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">void</span><span style="color: #000000; font-size: 10pt;">&nbsp;invalid_type()&nbsp;{<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;f&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-size: 10pt;">new</span><span style="color: #000000; font-size: 10pt;">&nbsp;CollectionFactory()<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;Stack&nbsp;s&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;(Stack)&nbsp;f.getCollection(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">Stack-like</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;s.add(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;result&nbsp;</span><span style="color: #000000; font-size: 10pt;">=</span><span style="color: #000000; font-size: 10pt;">&nbsp;s.search(</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">foo</span><span style="color: #000000; font-size: 10pt;">"</span><span style="color: #000000; font-size: 10pt;">)<br /></span><span style="color: #000000; font-size: 10pt;">}</span></div><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在清单5中，我必须对集合工厂返回的对象进行强制类型转换，这样才能允许我调用Stack类中的search()方法。但这种方式会产生一些局限性：当使类型静态化之后，Groovy的很多动态特性将无法工作。然而，上述救命证明了Groovy将继续进行改进，以弥合静态性与动态性之间的分歧。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 所有这些语言都有十分强大的元编程功能，所以更为严苛的类型化可以在事后再添加进来。例如，已有多个分支项目将选择性类型(selective type)引入到Clojure中。但一般认为选择性类型是可选的，它不是类型系统的一部分；它只是一个类型验证系统。</span><br /><br /><strong><span style="font-size: 10pt;">命令式 vs. 函数式</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另一个主要的比较维度就是命令式与函数式。命令式编程注重于单步执行的结构，在许多情况下，它是模仿了早期底层硬件的有益结构。函数式编程则注重将函数作为第一等的结构体，以试图将状态传递与可变性降低到最小。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Groovy在很大程度上是受Java的启发，它在根本上仍然是命令式语言。但从一开始，Groovy就加入了许多函数式命令的特性，并且以后还会加入更多的此类特性。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Scala则弥合了这两种编程范式，它同时支持这两种范式。在更偏向(也更鼓励)函数式编程的同时，Scala依然支持面向对象和命令式编程。因此，为了恰当地使用Scala，就要求团队要受到良好的培训，以确保你不会混用和随意地选择编程范式，在多范式编程语言中，这一直都是一个危险。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Clojure是铁杆的函数式编程语言。它也支持面向对象特性，使得它能够很容易地与其它JVM语言进行交互，它并不试图去弥合这两种范式之间的隔阂。相反，Clojure这种义无反顾的决策使它的设计者所考虑的语句成为很好的工程学实践。这些决策具有深远的影响，它使Clojure能够以开创性的方法去解决Java世界中一些挥之不去的问题(如并发)。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在学习这些新语言时所要求的许多思想上的转变就是源自于命令式与函数式之间的巨大差别，而这也正是本系列文章所要探索的最有价值的领域之一。</span><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开发者们正生活在一个多语言编程快速发展的世界中，在这种环境中，要求使用多种不同的语言去解决问题。学习高效地利用新语言可以帮助你决定哪种方法是合适的。即便你无法离开Java，它也会逐步地将下一代JVM语言中的特性纳入到Java中；现在看看这些新特性，就会使你在潜移默化之中掌握到未来的Java语言。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在本系列的下一篇文章中，我将开始通过探索Groovy，Scala和Clojure中的共通之处来对它们进行比较。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/395164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-02-06 00:04 <a href="http://www.blogjava.net/jiangshachina/archive/2013/02/06/395164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对Java解序列化早作防备(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 17 Jan 2013 14:39:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/394367.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/394367.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/394367.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">对Java解序列化早作防备</span></strong></div>&nbsp;&nbsp;&nbsp; 本文是<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中的<a href="http://www.ibm.com/developerworks/library/se-lookahead/index.html">一篇文章</a>，介绍了不使用加密与签章技术，如何防止对不可信数据输入的解序列化。(2013.01.18最后更新)<br /><br />&nbsp;&nbsp;&nbsp; Java序列化允许开发者将Java对象保存为二进制格式，以便将该对象持久化到一个文件中或将其在网络中进行传递。远程方法调用(RMI)使用序列化作为客户端与服务器端之间的通信媒介。当服务从客户端接收二进制数据，以及将输入的数据进行解序列化去构造Java实例时，就会产生多种安全问题。本文关注其中一种问题：骇客可能序列化另一个类的实例，并将其传给服务程序。那么服务程序就会解序列化该恶意对象，并很可能将该对象强制转换为服务所期望得到的合法类型，而这将导致异常的发生。然而，该异常对于确保数据安全性则显得太晚了。本文解释了为什么要以及怎样去实现一种安全的序列化。<br /><br /><strong><span style="font-size: 12pt;">脆弱的类</span></strong><br />&nbsp;&nbsp;&nbsp; 你的服务程序不可能反序列化任意类的对象。为什么不能呢？简单的回答是：因为在服务器端的类路径中可能存有被骇客利用的脆弱类。这些类所包含的代码为骇客造就了拒绝服务(DOS)的条件，或者--在极端情况下--会允许骇客注入任意代码。<br />&nbsp;&nbsp;&nbsp; 你可能会相信存在这种攻击的可能性，但考虑到一个典型的服务器端程序的类路径中存在太多的类，不仅包含你自己的代码，还包括Java核心类库，第三方的类库，以及其它的中间件或框架中的类库。另外，在应用程序的生命周期中，类路径可能会被改变，或者为了应对底层运行环境的变化，应用程序的类路径也可能被修改。当试图去利用这样的弱点时，通过传送多个序列化对象，骇客能够将这些操作组合到一块。<br />&nbsp;&nbsp;&nbsp; 我应该强调一下，仅当满足如下条件时，服务才会解序列化一个恶意对象：<br />&nbsp;&nbsp;&nbsp; 1. 恶意对象的类存在于服务器端的类路径中。骇客不可能随便地传递任意类的序列化对象，因为应用服务可能无法加载这个类。<br />&nbsp;&nbsp;&nbsp; 2. 恶意对象的类要么是可序列化的，要么是可外部化的。(即，服务器端的这个类要实现java.io.Serializable或java.io.Externalizable)<br /><br />&nbsp;&nbsp;&nbsp; 另外，通过从序列化流中直接复制数据，在不调用构造器的情况下，解序列化操作就能产生对象树，所以骇客不可能执行序列化对象类的构造器中的Java代码。<br />&nbsp;&nbsp;&nbsp; 但骇客还有其它途径在服务器端去执行代码。无论JVM在何时去解序列化一个对象，都将实现如下三个方法中的一个，都将调用并执行该方法中的代码：<br />&nbsp;&nbsp;&nbsp; 1. 方法readObject()，当标准的序列化机制不适用时，开发者一般就会用到该方法。例如，当需要对transient成员变量进行赋值时。<br />&nbsp;&nbsp;&nbsp; 2. 方法readResolve()，一般用于序列化单例对象。<br />&nbsp;&nbsp;&nbsp; 3. 方法readExternal()，用于外部化对象。<br /><br />&nbsp;&nbsp;&nbsp; 所以，如果在你的类路径中存在着使用上述方法的类，你就必须意识到骇客可能会在远程调用这些方法。此类攻击在过往曾被用于破坏Applet安全沙箱；同样地，相同的攻击技术也可用于服务器端应用。<br />&nbsp;&nbsp;&nbsp; 继续读下去，将会看到如何才能只允许应用服务对其期望的类的对象进行解序列化。<br /><br /><strong><span style="font-size: 12pt;">Java序列化二进制格式</span></strong><br />&nbsp;&nbsp;&nbsp; 一个对象被序列化之后，二进制数据将包含有元数据(指与数据的结构相关的信息，例如类的名称，成员的数量，以及成员的类型)，及对象数据本身。我将以一个简单的Bicycle类作为例子，如清单1所示，该类包含三个成员变量(id，name和nbrWheels)以及与之对应的set与get方法。<br /><strong>清单1. Bicycle类</strong><br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer;<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Bicycle&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;java.io.Serializable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">5754104541168320730L</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;id;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;String&nbsp;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;nbrWheels;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Bicycle(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;id,&nbsp;String&nbsp;name,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;nbrWheels)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.id&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;id;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.name&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.nbrWheels&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nbrWheels;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;getName()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setName(String&nbsp;name)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.name&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setId(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;id)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.id&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;id;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;getId()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;id;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;getNbrWheels()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;nbrWheels;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;setNbrWheels(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;nbrWheels)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.nbrWheels&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nbrWheels;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 在一个清单1所示类的实例被序列化之后，其数据流如清单2所示：<br /><strong>清单2. Bicycle类的序列化流</strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">000000</span><span style="color: #000000; ">:&nbsp;AC&nbsp;ED&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">05</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;2C&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;6F&nbsp;6D&nbsp;2E&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">62</span><span style="color: #000000; ">&nbsp;6D&nbsp;2E&nbsp;|&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;com.ibm.|<br /></span><span style="color: #000000; ">000016</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">62</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;2E&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">67</span><span style="color: #000000; ">&nbsp;2E&nbsp;4C&nbsp;6F&nbsp;6F&nbsp;6B&nbsp;</span><span style="color: #000000; ">41</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">68</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">64</span><span style="color: #000000; ">&nbsp;|ba.scg.LookAhead|<br /></span><span style="color: #000000; ">000032</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">44</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;6C&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;7A&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;2E&nbsp;</span><span style="color: #000000; ">42</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;|Deserializer.Bic|<br /></span><span style="color: #000000; ">000048</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">79</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;6C&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;4F&nbsp;DA&nbsp;AF&nbsp;</span><span style="color: #000000; ">97</span><span style="color: #000000; ">&nbsp;F8&nbsp;CC&nbsp;C0&nbsp;DA&nbsp;</span><span style="color: #000000; ">02</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">03</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">49</span><span style="color: #000000; ">&nbsp;|ycle&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;I|<br /></span><span style="color: #000000; ">000064</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">02</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">64</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">49</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">09</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">62</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">57</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">68</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;6C&nbsp;</span><span style="color: #000000; ">73</span><span style="color: #000000; ">&nbsp;|&#183;&#183;idI&#183;&#183;nbrWheels|<br /></span><span style="color: #000000; ">000080</span><span style="color: #000000; ">:&nbsp;4C&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">04</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;6D&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">74</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">12</span><span style="color: #000000; ">&nbsp;4C&nbsp;6A&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">76</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;2F&nbsp;|L&#183;&#183;name&#183;&#183;&#183;Ljava/|<br /></span><span style="color: #000000; ">000096</span><span style="color: #000000; ">:&nbsp;6C&nbsp;</span><span style="color: #000000; ">61</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">67</span><span style="color: #000000; ">&nbsp;2F&nbsp;</span><span style="color: #000000; ">53</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">74</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">72</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">67</span><span style="color: #000000; ">&nbsp;3B&nbsp;</span><span style="color: #000000; ">78</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">70</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;|lang/String</span><span style="color: #008000; ">;</span><span style="color: #008000; ">&#183;&#183;&#183;&#183;|</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">000112</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">01</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">74</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">00</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">08</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">55</span><span style="color: #000000; ">&nbsp;6E&nbsp;</span><span style="color: #000000; ">69</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">79</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">63</span><span style="color: #000000; ">&nbsp;6C&nbsp;|&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;Unicycl|<br /></span><span style="color: #000000; ">000128</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">65</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|e|</span></div><br />&nbsp;&nbsp;&nbsp; 对上述数据应用标准的对象序列化流协议，你将看到如清单3所示的序列化对象：<br /><strong>清单3. 被序列化的Bicycle对象的细节</strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">STREAM_MAGIC&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0xACED&nbsp;<br />STREAM_VERSION&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">5</span><span style="color: #000000; "><br />newObject<br />&nbsp;&nbsp;&nbsp;&nbsp;TC_OBJECT&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x73<br />&nbsp;&nbsp;&nbsp;&nbsp;newClassDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_CLASSDESC&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x72<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;className<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0x2C&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">44</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">59</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;com.ibm.ba.scg.LookAheadDeserializer.Bicycle<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;serialVersionUID&nbsp;(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0x4FDAAF97F8CCC0DA&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">5754104541168320730</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classDescInfo<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classDescFlags&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x02&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;SC_SERIALIZABLE<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;primitiveDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prim_typecode&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;I&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;integer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fieldName<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;id<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">1</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;primitiveDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prim_typecode&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;I&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;integer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fieldName<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">9</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">9</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;nbrWheels<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">2</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;objectDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj_typecode&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;L&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;object<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fieldName<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;&nbsp;name<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;className1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_STRING&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x74<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;0x12&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">18</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">18</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;Ljava/lang/String</span><span style="color: #008000; ">;<br /></span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classAnnotation<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_ENDBLOCKDATA&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x78<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;superClassDesc<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_NULL&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x70<br />&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">0</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;id<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">1</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nbrWheels<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classdata</span><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">2</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TC_STRING&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;byte)&nbsp;0x74<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;</span><span style="color: #000000; ">8</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">&nbsp;bytes)&nbsp;Unicycle</span></div><br />&nbsp;&nbsp;&nbsp; 从清单3中你可以看到该序列化对象的类型为com.ibm.ba.scg.LookAheadDeserializer.Bicycle，它的ID为0，只有一个轮子，即它是一个独轮车。 <br />&nbsp;&nbsp;&nbsp; 重点是这个二进制格式包含一种文件头，这就允许你对输入进行校验。<br /><br /><strong style="font-size: 12pt;">类校验</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; 如你在清单3中所看到的，在读取该二进制流时，在对象本身出现之前，首先会看到该序列化对象的类型描述。这种结构就允许实现自己的算法去读取类型描述，并依靠类的名称去决定是否继续读取该序列化流。幸运地是，通过使用Java提供的一个常用于定制类加载的"钩子"，你能很容易地实现该功能--即，覆盖resolveClass()方法。这个"钩子"方法非常适合用于提供定制的校验功能，无论序列化流何时包含了不被期望的类，你都可以用这个方法去抛出一个异常。你需要继承类java.io.ObjectInputStream，并覆盖其中的resolveClass()方法。清单4中的代码就利用该项技术确保只有Bicycle类的实例才可被解序列化。<br /><strong>清单4. 定制校验"钩子"程序</strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.IOException;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.InputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.InvalidClassException;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectInputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectStreamClass;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer.Bicycle;<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;LookAheadObjectInputStream&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;ObjectInputStream&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;LookAheadObjectInputStream(InputStream&nbsp;inputStream)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">(inputStream);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Only&nbsp;deserialize&nbsp;instances&nbsp;of&nbsp;our&nbsp;expected&nbsp;Bicycle&nbsp;class<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;resolveClass(ObjectStreamClass&nbsp;desc)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassNotFoundException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">desc.getName().equals(Bicycle.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">.getName()))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;InvalidClassException(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Unauthorized&nbsp;deserialization&nbsp;attempt</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;desc.getName());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">.resolveClass(desc);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 通过对com.ibm.ba.scg.LookAheadDeserializer类的实例调用readObject()方法，就可以防止对不被期望的对象进行解序列化操作。<br />&nbsp;&nbsp;&nbsp; 作为一个示例应用程序，清单5序列化了两个对象--一个是期望的类(com.ibm.ba.scg.LookAheadDeserializer.Bicycle)的实例，另一个是不被期望的类(java.io.File)的实例--然后使用清单4中的定制校验"钩子"程序去尝试它们进行解序列化。<br /><strong>清单5. 使用定制的"钩子"程序</strong><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ByteArrayInputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ByteArrayOutputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.File;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.IOException;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectInputStream;<br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.ObjectOutputStream;<br /><br /></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.ibm.ba.scg.LookAheadDeserializer.Bicycle;<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;LookAheadDeserializer&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;serialize(Object&nbsp;obj)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayOutputStream&nbsp;baos&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ByteArrayOutputStream();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;oos&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ObjectOutputStream(baos);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oos.writeObject(obj);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;buffer&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;baos.toByteArray();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oos.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baos.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;buffer;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Object&nbsp;deserialize(</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;buffer)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;IOException,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassNotFoundException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayInputStream&nbsp;bais&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ByteArrayInputStream(buffer);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;We&nbsp;use&nbsp;LookAheadObjectInputStream&nbsp;instead&nbsp;of&nbsp;InputStream</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectInputStream&nbsp;ois&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;LookAheadObjectInputStream(bais);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;obj&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ois.readObject();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ois.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bais.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;obj;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Serialize&nbsp;a&nbsp;Bicycle&nbsp;instance</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;serializedBicycle&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;serialize(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Bicycle(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Unicycle</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Serialize&nbsp;a&nbsp;File&nbsp;instance</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;serializedFile&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;serialize(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Pierre&nbsp;Ernst</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Deserialize&nbsp;the&nbsp;Bicycle&nbsp;instance&nbsp;(legitimate&nbsp;use&nbsp;case)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bicycle&nbsp;bicycle0&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(Bicycle)&nbsp;deserialize(serializedBicycle);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(bicycle0.getName()&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;has&nbsp;been&nbsp;deserialized.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Deserialize&nbsp;the&nbsp;File&nbsp;instance&nbsp;(error&nbsp;case)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bicycle&nbsp;bicycle1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(Bicycle)&nbsp;deserialize(serializedFile);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Exception&nbsp;ex)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(System.err);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><br />当运行该应用程序时，在试图去java.io.File的对象进行解序列化之前，JVM就抛出异常，如图1所示：<br /><strong>图1. 应用程序输出</strong><br /><img alt="" src="http://www.ibm.com/developerworks/library/se-lookahead/application-output.jpg" height="123" width="580" /><br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br />&nbsp;&nbsp;&nbsp; 本文向你展示了在序列化流中发现不被期望的类之后，若不使用加密，签章，或简单的成员变量校验等手段，如何能尽快地停止Java解序列化操作。<br />&nbsp;&nbsp;&nbsp; 需要记住的是，整棵对象树(根对象，及其所有的成员对象)是在解序列化过程中进行组建的。在更为复杂的情况下，你可能必须允许更多的类可被解序列化。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/394367.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-01-17 22:39 <a href="http://www.blogjava.net/jiangshachina/archive/2013/01/17/394367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索Java语言与JVM中的Lambda表达式(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 01 Jan 2013 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/393674.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/393674.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/393674.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">探索Java语言与JVM中的Lambda表达式</strong></div>&nbsp;&nbsp;&nbsp; Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性，本文是2012年度最后一期<a href="http://www.oracle.com/technetwork/java/javamagazine/index.html?origref=http://java.net/projects/java-magazine">Java Magazine</a>中的<a href="http://www.oraclejavamagazine-digital.com/javamagazine/20121112#pg35">一篇文章</a>，它介绍了Lamdba的设计初衷，应用场景与基本语法。(2013.01.07最后更新)<br /><br />&nbsp;&nbsp;&nbsp; Lambda表达式，这个名字由该项目的专家组选定，描述了一种新的函数式编程结构，这个即将出现在Java SE 8中的新特性正被大家急切地等待着。有时你也会听到人们使用诸如闭包，函数直接量，匿名函数，及SAM(Single Abstract Method)这样的术语。其中一些术语彼此之间会有一些细微的不同，但基本上它们都指代相同的功能。<br />&nbsp;&nbsp;&nbsp; 虽然一开始会觉得Lambda表达式看起来很陌生，但很容易就能掌握它。而且为了编写可完全利用现代多核CPU的应用程序，掌握Lambda表达式是至关重要的。<br />&nbsp;&nbsp;&nbsp; 需要牢记的一个关键概念就是，Lambda表达式是一个很小且能被当作数据进行传递的函数。需要掌握的第二个概念就是，理解集合对象是如何在内部进行遍历的，这种遍历不同于当前已有的外部顺序化遍历。<br />&nbsp;&nbsp;&nbsp; 在本文中，我们将向你展示Lambda表达式背后的动因，应用示例，当然，还有它的语法。<br /><strong><br /><span style="font-size: 12pt;">为什么你需要Lambda表达式</span></strong><br />&nbsp;&nbsp;&nbsp; 程序员需要Lambda表达式的原因主要有三个：<br />&nbsp;&nbsp;&nbsp; 1. 更紧凑的代码<br />&nbsp;&nbsp;&nbsp; 2. 通过提供额外的功能对方法的功能进行修改的能力<br />&nbsp;&nbsp;&nbsp; 3. 更好地支持多核处理<br /><br /><strong>更紧凑的代码</strong><br />&nbsp;&nbsp;&nbsp; Lambda表达式以一种简洁的方式去实现仅有一个方法的Java类。<br />&nbsp;&nbsp;&nbsp; 例如，如果代码中有大量的匿名内部类--诸如用于UI应用中的监听器与处理器实现，以及用于并发应用中的Callable与Runnable实现--在使用了Lambda表达式之后，将使代码变得非常短，且更易于理解。<br /><br /><strong>修改方法的能力</strong><br />&nbsp;&nbsp;&nbsp; 有时，方法不具备我们想要的一些功能。例如，Collection接口中的contains()方法只有当传入的对象确实存在于该集合对象中时才会返回true。但我们无法去干预该方法的功能，比如，若使用不同的大小写方案也可以认为正在查找的字符串存在于这个集合对象中，我们希望此时contains()方法也能返回true。<br />&nbsp;&nbsp;&nbsp; 简单点儿说，我们所期望做的就是"将我们自己的新代码传入"已有的方法中，然后再调用这个传进去的代码。Lambda表达式提供了一种很好的途径来代表这种被传入已有方法且应该还会被回调的代码。<br /><br /><strong>更好地支持多核处理</strong><br />&nbsp;&nbsp;&nbsp; 当今的CPU具备多个内核。这就意味着，多线程程序能够真正地被并行执行，这完全不同于在单核CPU中使用时间共享这种方式。通过在Java中支持函数式编程语法，Lambda表达式能帮助你编写简单的代码去高效地应用这些CPU内核。<br />&nbsp;&nbsp;&nbsp; 例如，你能够并行地操控大集合对象，通过利用并行编程模式，如过滤、映射和化简(后面将会很快接触到这些模式)，就可使用到CPU中所有可用的硬件线程。<br /><br /><strong><span style="font-size: 12pt;">Lambda表达式概览</span></strong><br />&nbsp;&nbsp;&nbsp; 在前面提到的使用不同大小写方案查找字符串的例子中，我们想做的就是把方法toLowerCase()的表示法作为第二个参数传入到contains()方法中，为此需要做如下的工作：<br />&nbsp;&nbsp;&nbsp; 1. 找到一种途径，可将代码片断当作一个值(某种对象)进行处理<br />&nbsp;&nbsp;&nbsp; 2. 找到一种途径，将上述代码片断传递给一个变量<br />&nbsp;&nbsp;&nbsp; 换言之，我们需要将一个程序逻辑包装到某个对象中，并且该对象可以被进行传递。为了说的更具体点儿，让我们来看两个基本的Lambda表达式的例子，它们都是可以被现有的Java代码进行替换的。<br /><br /><strong>过滤</strong><br />&nbsp;&nbsp;&nbsp; 你想传递的代码片断可能就是过滤器，这是一个很好的示例。例如，假设你正在使用(Java SE 7预览版中的)java.io.FileFilter去确定目录是否隶属于给定的路径，如清单1所示，<br /><em>清单1</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">File&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/an/interesting/location/</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />FileFilter&nbsp;directoryFilter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileFilter()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;accept(File&nbsp;file)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;file.isDirectory();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />};<br />File[]&nbsp;directories&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;dir.listFiles(directoryFilter);</span></div><br />&nbsp;&nbsp;&nbsp; 在使用Lambda表达式之后，代码会得到极大的简化，如清单2所示，<br /><em>清单2</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">File&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/an/interesting/location/</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />FileFilter&nbsp;directoryFilter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(File&nbsp;f)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;f.isDirectory();<br />File[]&nbsp;directories&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;dir.listFiles(directoryFilter);</span></div><br />&nbsp;&nbsp;&nbsp; 赋值表达式的左边会推导出类型(FileFilter)，右边则看起来像FileFilter接口中accept()方法的一个缩小版，该方法会接受一个File对象，在判定f.isDirectory()之后返回一个布尔值。<br />&nbsp;&nbsp;&nbsp; 实际上，由于Lambda表达式利用了类型推导，基于后面的工作原理，我们还可以进一步简化上述代码。编译器知道FileFilter只有唯一的方法accept()，所以它必定是该方法的实现。我们还知，accept()方法只需要一个File类型的参数。因此，f必定是File类型的。如清单3所示，<br /><em>清单3</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">File&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;File(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/an/interesting/location/</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />File[]&nbsp;directories&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;dir.listFiles(f&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;f.isDirectory());</span></div><br />&nbsp;&nbsp;&nbsp; 你可以看到，使用Lambda表达式会大幅降低模板代码的数量。<br />&nbsp;&nbsp;&nbsp; 一旦你习惯于使用Lambda表达式，它会使逻辑流程变得非常易于阅读。在达到这一目的的关键方法之一就是将过滤逻辑置于使用该逻辑的方法的侧边。<br /><br /><strong>事件处理器</strong><br />&nbsp;&nbsp;&nbsp; UI程序是另一个大量使用匿名内部类的领域。让我们将一个点击监听器赋给一个按钮，如清单4所示，<br /><em>清单4</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Button&nbsp;button&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Button();<br />button.addActionListener(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ActionListener()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ui.showSomething();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />});</span></div><br />&nbsp;&nbsp;&nbsp; 这多么代码无非是说"当点击该按钮时，调用该方法"。使用Lambda表达式就可写出如清单5所示的代码，<br /><em>清单5</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">ActionListener&nbsp;listener&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;event&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{ui.showSomething();};<br />button.addActionListener(listener);</span></div><br />&nbsp;&nbsp;&nbsp; 该监听器在必要时可被复用，但如果它仅需被使用一次，清单6中的代码则考虑了一种很好的方式。<br /><em>清单6</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">button.addActionListener(event&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{ui.showSomething();});</span></div><br />&nbsp;&nbsp;&nbsp; 在这个例子中，这种使用额外花括号的语法有些古怪，但这是必须的，因为actionPerformed()方法返回的是void，后面我们会看到与此有关的更多内容。<br />&nbsp;&nbsp;&nbsp; 现在让我们转而关注Lambda表达式在编写处理集合对象的新式代码中所扮演的角色，尤其是当针对两种编程风格，外部遍历与内部遍历，之间的转换的时候。<br /><br /><strong><span style="font-size: 12pt;">外部遍历 vs. 内部遍历</span></strong><br />&nbsp;&nbsp;&nbsp; 到目前为止，处理Java集合对象的标准方式是通过外部遍历。之所以称其为外部遍历，是因为要使用集合对象外部的控制流程去遍历集合所包含的元素。这种传统的处理集合的方式为多数Java程序员所熟知，尽管他们并不知道或不使用外部遍历这个术语。<br />&nbsp;&nbsp;&nbsp; 如清单7所示，Java语言为增强的for循环构造了一个外部迭代器，并使用这个迭代器去遍历集合对象，<br /><em>清单7</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;myStrings&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;getMyStrings();<br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(String&nbsp;myString&nbsp;:&nbsp;myStrings)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(myString.contains(possible))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(myString&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;contains&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000;">&nbsp;possible);<br />}</span></div>&nbsp;&nbsp;&nbsp; 使用这种方法，集合类代表着全部元素的一个"整体"视图，并且该集合对象还能支持对任意元素的随机访问，程序员可能会有这种需求。<br />&nbsp;&nbsp;&nbsp; 基于这种观点，可通过调用iterator()方法去遍历集合对象，该方法将返回集合元素类型的迭代器，该迭代器是针对同一集合对象的更具限制性的视图。它没有为随机访问暴露任何接口；相反，它纯粹是为了顺序地访问集合元素而设计的。这种顺序本性使得当你试图并发地访问集合对象时就会造成臭名昭著的ConcurrentModificationException。<br />&nbsp;&nbsp;&nbsp; 另一种可选的方案就是要求集合对象要能够在内部管理迭代器(或循环)，这种方案就是内部遍历，当使用Lambda表达式时会优先选择内部遍历。<br />&nbsp;&nbsp;&nbsp; 除了新的Lambda表达式语法以外，Lambda项目还包括一个经过大幅升级的集合框架类库。这次升级的目的是为了能更易于编写使用内部遍历的代码，以支持一系列众所周知的函数式编程典范。<br /><br /><strong><span style="font-size: 12pt;">使用Lambda的函数式编程</span></strong><br />&nbsp;&nbsp;&nbsp; 曾经，大多数开发者发现他们需要集合能够执行如下一种或几种操作：<br />&nbsp;&nbsp;&nbsp; 1. 创建一个新的集合对象，但要过滤掉不符合条件的元素。<br />&nbsp;&nbsp;&nbsp; 2. 对集合中的元素逐一进行转化，并使用转化后的集合。<br />&nbsp;&nbsp;&nbsp; 3. 创建集合中所有元素的某个属性的总体值，例如，合计值与平均值。这样的任务(分别称之为过滤，映射和化简)具有共通的要点：它们都需要处理集合中的每个元素。<br />&nbsp;&nbsp;&nbsp; 程序无论是判定某个元素是否存在，或是判断元素是否符合某个条件(过滤)，或是将元素转化成新元素并生成新集合(映射)，或是计算总体值(化简)，关键原理就是"程序必须处理到集合中的每个元素"。<br />&nbsp;&nbsp;&nbsp; 这就暗示我们需要一种简单的途径去表示用于内部遍历的程序。幸运地是，Java SE 8为此类表示法提供了构建语句块。<br /><br /><strong style="font-size: 12pt;">Java SE 8中支持基本函数式编程的类</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; Java SE 8中的一些类意在被用于实现前述的函数式典范，这些类包括Predicate，Mapper和Block--当然，还有其它的一些类--它们都在一个新的java.util.functions包中。<br />&nbsp;&nbsp;&nbsp; 看看Predicate类的更多细节，该类常被用于实现过滤算法；将它作用于一个集合，以返回一个包含有符合谓语条件元素的新集合。何为谓语，有很多种解释。Java SE 8认为谓语是一个依据其变量的值来判定真或假的方法。<br />&nbsp;&nbsp;&nbsp; 再考虑一下我们之前看过的一个例子。给定一个字符串的集合，我们想判定它是否包含有指定的字符串，但希望字符串的比较是大小写不敏感的。<br />&nbsp;&nbsp;&nbsp; 在Java SE 7中，我们将需要使用外部遍历，其代码将如清单8所示，<br /><em>清单8</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printMatchedStrings(List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;myStrings)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;out&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(String&nbsp;s:&nbsp;myStrings)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(s.equalsIgnoreCase(possible))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.add(s);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;log(out);<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 而在即将发布的Java SE 8中，我们使用Predicate以及Collections类中一个新的助手方法(过滤器)就可写出更为紧凑的程序，如清单9所示，<br /><em>清单9</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printMatchedStrings()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;Predicate</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;matched&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;s&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;s.equalsIgnoreCase(possible);<br />&nbsp;&nbsp;&nbsp;&nbsp;log(myStrings.filter(matched));<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 事实上，如果使用更为通用的函数式编程风格，你只需要写一行代码，如清单10所示，<br /><em>清单10</em><br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printMatchedStrings()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;log(myStrings.filter(s&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;s.equalsIgnoreCase(possible)));<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 如你所见，代码依然非常的易读，并且我们也体会到了使用内部遍历的好处。<br />&nbsp;&nbsp;&nbsp; 最后，让我们讨论一下Lambda表达式语法的更多细节。<br /><br /><strong><span style="font-size: 12pt;">Lambda表达式的语法规则</span></strong><br />&nbsp;&nbsp;&nbsp; Lambda表达式的基本格式是以一个可被接受的参数列表开头，以一些代码(称之为表达式体/body)结尾，并以箭头(-&gt;)将前两者分隔开。<br />&nbsp;&nbsp;&nbsp; 注意：Lambda表达式的语法仍可能会面临改变，但在撰写本文的时候，下面示例中所展示的语法是能够正常工作的。<br />&nbsp;&nbsp;&nbsp; Lambda表达式非常倚重类型推导，与Java的其它语法相比，这显得极其不同寻常。<br />&nbsp;&nbsp;&nbsp; 让我们进一步考虑之前已经看过的一个示例(请见清单11)。如果看看ActionListener的定义，可以发现它只有一个方法(请见清单12)。<br /><em>清单11</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">ActionListener&nbsp;listener&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;event&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;{ui.showSomething();};</span></div><br /><em>清单12</em><br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;ActionListener&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000;">&nbsp;actionPerformed(ActionEvent&nbsp;event);<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 所以，在清单11右侧的Lambda表达式，能够很容易地理解为"这是针对仅声明单个方法的接口的方法定义"。注意，仍然必须要遵守Java静态类型的一般规则；这是使类型推导能正确工作的唯一途径。<br />&nbsp;&nbsp;&nbsp; 据此可以发现，使用Lambda表达式可以将先前所写的匿名内部类代码转换更紧凑的代码。<br />&nbsp;&nbsp;&nbsp; 还需要意识到有另一个怪异的语法。让我们再回顾下上述示例，如清单13所示，<br /><em>清单13</em><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">FileFilter&nbsp;directoryFilter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(File&nbsp;f)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;f.isDirectory();</span></div><br />&nbsp;&nbsp;&nbsp; 仅一瞥之，它看起来与ActionListener的示例相似，但让我们看看FileFilter接口的定义(请见清单14)。accept()方法会返回一个布尔值，但并没有一个显式的返回语句。相反，该返回值的类型是从Lambda表达式中推导出来的<br /><em>清单14</em><br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;FileFilter&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000;">&nbsp;accept(File&nbsp;pathname);<br />}</span></div><br />&nbsp;&nbsp;&nbsp; 这就能解释，当方法返回类型为void时，为什么要进行特别处理了。对于这种情形，Lambda表达式会使用一对额外的花括号去包围住代码部分(表达式体/body)。若没有这种怪异的语法，类型推导将无法正常工作--但你要明白，这一语法可能会被改变。<br />&nbsp;&nbsp;&nbsp; Lambda表达式的表达式体可以包含多条语句，对于这种情形，表达式体需要被小括号包围住，但"被推导出的返回类型"这种语法将不启作用，那么返回类型关键字就必不可少。<br />&nbsp;&nbsp;&nbsp; 最后还需要提醒你的是：当前，IDE似乎还不支持Lambda语法，所以当你第一次尝试Lambda表达式时，必须要格外注意javac编译器抛出的任何警告。<br /><br /><strong><span style="font-size: 12pt;">结论</span></strong><br />&nbsp;&nbsp;&nbsp; Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性。应用得当，Lambda表达式可使你写出简洁的代码，为已有方法增加额外的功能，并能更好地适应多核处理器。到目前为止，我们能肯定的是，你正急切地想去尝试Lambda表达式，所以咱也别啰嗦了...<br />&nbsp;&nbsp;&nbsp; 你可以从Lambda项目的主页中获得包含有Lambda表达式的Java SE 8快照版。同样地，在试用二进制包时，你也应该先阅读一下"Lambda项目状态"的相关文章，可以在此处找到它们。<br /><br /><span style="color: red;"></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/393674.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2013-01-01 16:26 <a href="http://www.blogjava.net/jiangshachina/archive/2013/01/01/393674.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>"一致性相等"的陷阱(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 06 Dec 2012 15:14:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/392569.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/392569.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/392569.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt;">"一致性相等"的陷阱</span></strong></div><span style="font-size: 10pt;">关于Object类中的equals()方法与Comparable接口中的compareTo()方法之间有何种关联，之前还真没考虑过。通过java.net看到<a href="http://blog.joda.org/2012/11/pitfalls-of-consistent-with-equals.html">此文</a>之后，收获了一点儿新知识，希望大家也能如此。(2012.12.09最后更新)</span><br />
<br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">方法equals()与Comparable接口中的compareTo()方法是Java中最基本的两个方法之一，然而它们的定义却围绕着"与相等一致"这一有趣的概念。</span><br />
<span style="font-size: 10pt;"><br />
<strong>equals()方法</strong></span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">Java中的equals()方法既明确，又模糊。Java清楚地定义了如何准确地检验一个equals()方法是可用的。一个恰当的equals()方法必须是自反的，对称的，可传递的，一致的，并能处理null引用。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">然而equals()方法又是不清晰的。Javadoc说到，该方法指定了其它对象是"等于"这个对象的。注意，"等于"是放在引号中的。此处的关键就是，它没有定义如何去判定这种相等性。</span><br />
<ul><li><span style="font-size: 10pt;">对象的一致性(==)默认是继承自Object类</span></li><li><span style="font-size: 10pt;">对象的整体可观测的状态，例如，若两个对象是相等的，那么在应用的其它部分可以用一个对象去替代另一个对象。</span></li><li><span style="font-size: 10pt;">对象信息中的某些部分，如ID，使得检验对象相等性在逻辑上是有意义的。</span></li></ul>

<strong><span style="font-size: 10pt;">compareTo()方法</span></strong><br />&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">Comparable接口定义了可比较性的概念。Javadoc指出compareTo()方法"强制设定了每个实现了该接口的类的对象的全部顺序"。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">实现了Comparable接口的类有一个天然的排序，这可便于存储，也能在不使用单独的Comparator的情况下，用于像TreeSet和TreeMap这样的集合对象。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">该接口的定义明晰，它要求其实现必须确保对称性与传递性，就像equals()方法那样。</span><br />
<br />
<strong><span style="font-size: 10pt;">一致性/非一致性相等</span></strong><br />
<span style="font-size: 10pt;">Comparable接口有如下<a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html">描述</a>：</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">类C的天然排序意味着要与equals()方法保持一致，只有当且仅当e1.compareTo(e2) == 0与e1.equals(e2)有相同的布尔值。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">基本上，这就要求由compareTo()定义的相等性与equals()方法定义的相等性具有相同的概念(除去有null的情况)。乍一看，该要求很简单，但实际上它有其复杂性，后面将会讨论到。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">当考虑到操作符重载时，这种定义就特别有用。若我们假设有一种类Java语言，在这种语言中，==并不表示对象的同一性，而是通过方法去进行比较，大于/小于操作符也是如此，问题是调什么样的方法。在类Java语言中大于/小于天然地就要基于compareTo()方法，而==则要调用equals()方法。</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;our&nbsp;new&nbsp;Java-like&nbsp;language</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(a&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;b)&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Less</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;translation&nbsp;ignoring&nbsp;nulls:&nbsp;if&nbsp;(a.compareTo(b)&nbsp;&lt;&nbsp;0)</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(a&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;b)&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Greater</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;translation&nbsp;ignoring&nbsp;nulls:&nbsp;if&nbsp;(a.compareTo(b)&nbsp;&gt;&nbsp;0)</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(a&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;b)&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Equal</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;translation&nbsp;ignoring&nbsp;nulls:&nbsp;if&nbsp;(a.equals(b))</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Exception(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Impossible&nbsp;assuming&nbsp;no&nbsp;nulls?</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">但如果compareTo()方法不是"一致性相等"，那么上述代码将会抛出异常，因为当a.equals(b)为false时，a.compareTo(b)会返回0。</span><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在集合，如TreeMap，中还会发生其它问题：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Foo&nbsp;class&nbsp;is&nbsp;"inconsistent&nbsp;with&nbsp;equals"</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">assert</span><span style="color: #000000; ">&nbsp;foo1.equals(foo2)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">;<br /></span><span style="color: #0000FF; ">assert</span><span style="color: #000000; ">&nbsp;foo1.compareTo(foo2)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;<br />TreeMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Foo,&nbsp;String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;map&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />map.put(foo1,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />map.put(foo2,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">当使用equals()方法时，这两个对象不相等，但使用compareTo()时，它们却相等。在这种情况下，该Map的元素个数将为1，而非0。</span><br />&nbsp; &nbsp;
<span style="font-size: 10pt;">由于这些"一致性相等"的问题，Javadoc说道"强烈建议(尽管并不要求)天然排序规则要与equals()方法保持一致"。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">JDK中的许多类为了符合"一致性相等"这一规范而实现了Comparable接口。这些类包括Byte，Short，Integer，Long，Character和String。</span><br />
<br />
<span style="font-size: 10pt;">还有些更有趣的类：</span><br />
<span style="font-size: 10pt;">&nbsp; &nbsp; BigDecimal--肯定是"非一致性相等"，比如4.00与4.0不一致，但进行比较时，认为它们是一样的。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Double/Float--该类显式地提供了排序规则，并为正零和负零，以及NaN都提供了相等性检查，以确保它的compareTo()方法符合"一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp; &nbsp; CharSet--该类基于ID或名称。equals()方法对待字段串是大小写敏感的，但compareTo()方法却不这样。虽然名称一般会符合某种标准，但这是一种值得怀疑的"一致性"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; *Buffer(nio)--该簇类的比较基于缓冲存放的内容，在我的测试中equals()和compareTo()是"一致的"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Rdn(ldap)--该类的比较基于状态的标准化格式，因此也是"一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; ObjectStreamField(序列化)--该类的比较基于名称，但会首先对基本数据类型进行排序。因为没有覆盖equals()方法，所以是"非一致性相等"。</span><br />&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">注意：对于大多数的例子，我都不得不查看其源代码或编写测试程序以确定该类是不是符合"一致性相等"。这儿有一个不错地清理Javadoc和检验UUID equals()方法的Adopt-a-JDK任务。</span><br />
<br />
<strong><span style="font-size: 10pt;">JSR-310</span></strong><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">一直看到许多关于BigDecimal的问题，已有计划将<a href="https://github.com/ThreeTen/threeten">JSR-310</a>中的类改造成"一致性相等"，最近的一些<a href="https://github.com/ThreeTen/threeten/issues/132">帖子</a>显示这将造成多么大的争议。</span><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">基本上，为某些类定义equals()和compareTo()看起来很容易。LocalDate表示某单一日历系统中的某个日期，所以它有一个显而易见的排序算法和相等规则。LocalTime则表示某个时刻，所以它也有一个明显的排序算法和相等规则。Instant表示时间线上的某个时刻，那么它的排序与相等也是显见的。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">但在其它的情况下，这就不是那么显而易见了。考虑这样一个类OffsetDateTime：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">dt1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;OffsetDateTime.parse(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">2012-11-05T06:00+01:00</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />dt2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;OffsetDateTime.parse(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">2012-11-05T07:00+02:00</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">这样的两个日期-时刻对象代表时间线上一个相同的时刻点，但它们有不同的本地时，而且相对的UTC/格林威治时间的偏移量也不相同。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">那么就有一个问题要留给读者们...你更倾向于如下哪种观点...</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 1. dt1不等于dt2，compareTo()分别比较本地时与偏移量，使用"一致性相等"(使用独立的Comparator基于时刻对其进行排序)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 2. dt1不等于dt2，compareTo()基于时间线的上时刻点，使用"非一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 3. dt1等于dt2，compareTo()基于时间线的上时刻点，使用"一致性相等"。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 4. dt1等于dt2，且不实现Comparable接口。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 5. dt1不等于dt2，且不实现Comparable接口。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">我个人更倾向于让dt1.equals(dt2)返回true这种方案，但我仍持开放态度。</span><br />&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">顺便地，也可以将这个问题提给BigDecimal，如果你能修改这个类，使其符合"一致性相等"，你会修改它的equals()方法，还是compareTo()方法？</span><br /><br />
</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/392569.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2012-12-06 23:14 <a href="http://www.blogjava.net/jiangshachina/archive/2012/12/06/392569.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应该使用32位还是64位的JVM？(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 26 Nov 2012 15:41:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/392047.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/392047.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/392047.html</trackback:ping><description><![CDATA[<div>
<div align="center"><strong><span style="font-size: 14pt;">应该使用32位还是64位的JVM？</span></strong></div>
应用使用32位的JVM，还是应该使用64位的JVM，我自己以前还真没想过，大家都想过了吗？本文是<a href="http://plumbr.eu">Plumbr</a>中的一篇<a href="http://plumbr.eu/blog/should-i-use-32-or-64-bit-jvm">博文</a>，为大家提了个醒。(2012.11.27最后更新)<br />
<br />&nbsp;&nbsp;&nbsp; 在我的企业级软件开发职业生涯中，我多次面对这个问题。每隔一段时间我不得不建议配置一个新的特定环境。而往往该问题部分与"我应该使用32位还是64位JVM？"这一问题有关。老实说，一开始我是靠掷硬币来解决的，而不是给出一个合理的答案。(抱歉，兄弟们！)但现在我对这个问题有了更多的领悟，并想与你们分享。<br />
&nbsp;&nbsp;&nbsp; 第一步--越多越好，对吗？即如此，因为64&gt;32，所以答案很简单：如果可能的话，应该总是选择64位？好吧，请耐心点儿。64位架构的坏处是，相同的数据结构会消耗更多的内存，甚至是多很多。我们的测评显示，根据不同的JVM版本和操作系统版本，以及相应的硬件架构，最终会用掉比32位架构多30-50%的堆内存。更大的堆也会造成更长的GC暂停，这会对应用程序造成潜在的影响--在4.5G的堆上执行完全GC肯定会比在3G的堆上执行花费更长的时间。所以，仅仅是因为64比32大就去赶64位JVM的时髦，肯定是错误的。<br />
&nbsp;&nbsp;&nbsp; 但，什么时候才是使用64位JVM的好时机呢？多数情况下，要看堆的大小。在不同的平台下，你很快就得面对32位JVM堆内存的上限问题。下表列出了在不同平台下的这种限制：<br />
Linux&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2GB &nbsp;&nbsp; &nbsp;&nbsp; 特定内核，如hugemem，可以达到3G<br />
Windows&nbsp;&nbsp;&nbsp; 1.5GB &nbsp;&nbsp; 使用"/3G"的启动参数并使用/LARGEADDRESSAWARE参数去编译JRE，则可提高到3G<br />
Mac OS X&nbsp;&nbsp; 3.8GB &nbsp;&nbsp; 警告--未能找到老的Mac，所以没有对其进行测试<br />
<br />
&nbsp;&nbsp;&nbsp; 那么，这有什么坏处？我打赌，你肯定见过在16G的RAM上运行32位的机器。问题就在于，在只有16G RAM的Windows系统中，JVM只能分配到少于10%的内存。<br />
&nbsp;&nbsp;&nbsp; 主因--地址空间。在32位系统中，理论上可以为每个进程分配4G内存。而Windows系统对地址空间的处理使这一理论值无法达到。Windows将进程的地址空间砍掉了一半。一半留给了内核(用户进程无法使用它)，另一半则留给了用户。无论系统中有多大的RAM，32位进程只能使用到2G的RAM。更糟的是--地址空间必须是连续的，所以在实践中，Windows系统最多只为你剩下了1.5-1.8G的堆内存。<br />
&nbsp;&nbsp;&nbsp; 有一个在32位Windows系统中减少内核空间并增加用户空间的窍门，即，可以在boot.ini系统使用/3GB参数。然而，为了能确保它有效，必须使用/LARGEADDRESSAWARE开关去编译或链接JVM。<br />
&nbsp;&nbsp;&nbsp; 不幸地是，至少对于HotSpot JVM无法做到这一点。直到最新的JDK 1.7版本，HotSpot JVM仍未使用该选项进行编译。但又幸运地是，如果你运行2006年之后的JRockit版本，就能享用到2.8-2.9G的堆大小。<br />
&nbsp;&nbsp;&nbsp; 那么，我们是否可以得出结论，如果你的应用要求大于2-3G的内存，你就总是应该运行64位的JVM？也许。但你也必须要清楚应用的场景。我们已经介绍了使用64位JVM的坏处--增加的堆消耗，以及更长的GC中断。让我们分析一下原因。<br />
&nbsp;&nbsp;&nbsp; <strong>问题1：64位JVM需要多出30-50%的内存。</strong>为什么会如此呢？主要是因为内存是以64位架构进行部局。首先，在64位JVM中，对象头有12字节。其次，对象引用会占用4字节或8字节，实际值取决于JVM的参数与堆的大小。相较于32位JVM的8字节对象头和4字节对象引用，毫无疑问会增加一些开销。在我们<a href="http://plumbr.eu/blog/how-much-memory-do-i-need-part-2-what-is-shallow-heap">之前发布的博客</a>中你会找到更多关于如何计算对象内存的相关信息。<br />
&nbsp;&nbsp;&nbsp; <strong>问题2：更长的垃圾收集中断。</strong>构建更大的堆意味着GC要做更多的工作去清理无用的对象。这意味着，在实际应用中构建大于12-16G的堆时，你必须要特别小心。没有很好的性能调优与测评，你很容易就会引起一个耗时数分钟的完全GC。在应用程序的非关键潜在危险场景中，通过对吞吐量的优化或许能解决这一问题，但在多数情况下，它会造成程序中断。<br />
&nbsp;&nbsp;&nbsp; 当我需要更大的内存且又不希望引入由64位架构所造成的开销，那该怎么办呢？在我们<a href="http://plumbr.eu/blog/increasing-heap-size-beware-of-the-cobra-effect">以前的一篇博文</a>中已经涉及了这个问题--通过对堆的分区，GC调优，构建不同的JVM，或对堆分配不同的内存，就可以避免这一问题。<br />
&nbsp;&nbsp;&nbsp; 最后，让我们重申，你应该总是要意识到选择64位JVM的后果，但也不要惧怕这一选择。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/392047.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2012-11-26 23:41 <a href="http://www.blogjava.net/jiangshachina/archive/2012/11/26/392047.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaOne 2012 Strategy Keynote(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/10/06/389103.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 06 Oct 2012 11:26:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/10/06/389103.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/389103.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/10/06/389103.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/389103.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/389103.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">JavaOne 2012 Strategy Keynote<br /></span></strong></div><span style="font-size: 10pt;">本文是Oracle官方针对今年JavaOne大会战略主旨演讲的一篇<a href="https://blogs.oracle.com/javaone/entry/javaone_2012_sunday_strategy_keynote">博客</a>，总结了过去一年中Java在不同领域的进展，并展望了其在今后一段时期内的计划。(2012.10.06最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 周日在Masonic Auditorium举行的战略主旨演讲中，中间件与Java开发的执行副主裁Hasan Rizvi阐述了今年JavaOne的主题：创造Java的未来。这意味着作为最流行的，最完备的，最有成效的，最安全的且最富创新的开发平台，Java仍继续扮演着这一角色。他说到："你们中的许多人已将你们的生意与职业投资到了Java之上，我们也已将我们的生意投资到了Java之上"。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Rizvi详述了他们认为的Java成功的三个至关重要的因素：技术创新，社区参与，以及Oracle的领导/管理(臭屁*_*)。他提供了过去一年中发生在上述三个方面中的实例：OS X与Linux ARM对Java SE的支持；年底对JavaFX的开源；Java Embedded Suite 7.0中间件平台的发布；Java EE端多个版本的发布。JCP进程还在继续，有了新的JSR活动，去年以来，JUG的参与度与增加了25%。同时，Oracle继续它在技术与社区发展/拓展方面的承诺--去年在世界不同的地方有四场区域性的JavaOne会议，以及Java Magazine的发布，该杂志目前有超过12万的注册用户。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 随后，Java SE开发的副总裁Georges Saab回顾了Java SE 7的特性--这是Oracle管理Java SE以来发布的第一个主版本，它包含差不多每月发布的更新，这些更新修复了数以百计的缺陷，还有性能提升与新特性。Saab指出开发者，独立软件提供商，以及服务提供商们已经很快地适应了这个平台。他也特别提到Oracle的全Fusion中间件产品线已受Java SE 7的支持。支持Java SE 7的平台也有所增加--从Windows，Linux，Solaris到OS X，Linux ARM，以及新兴的ARM微服务器市场。Saab谈到："去年，新增的支持Java的平台数就与过去十年中支持Java的平台数相同"。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Saab也探讨了即将到来的JDK 8--包括Lambda项目，Nashorn项目(一个运行在JVM的现代JavaScript实现)，及其它。他特别提到，Nashorn的功能已在内部应用于NetBeans 7.3，并宣称他们将把这一实现捐献给OpenJDK。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java Client，ME与Card的副总裁Nandini Ramani讨论了与JavaFX 2.0的最新消息--针对Windows，OS X与Linux的发布，FX Scene Builder工具的发布，NetBeans 7.3中的JavaFX WebView组件，以及OpenJDK中的OpenJFX组件。Nandini宣布自周日起可以下载针对Linux ARM的JavaFX(开发者预览版)，针对Linux的Scene Builder也一样可以。她提醒到，针对明年将发布的JDK 8，JavaFX将支持3D，也会支持第三方组件的集成。Navis高级软件工程师Avinder Brar与Canoo院士Dierk Konig随后登台演示了JavaFX的相关特性，该演示程序利用了Canoo刚刚开源的Dolphin技术，其特性丰富，还有不少的动画，是一个实时的货品管理应用。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Saab还探讨了Java SE 9及更远的未来--Jigsaw模块化，针对与OSGi协作遥Penrose项目，提升Java在云中的多租期(multi-tenancy)，以及Sumatra项目。HSA基金会主席与AMD院士探讨了异构计算平台，这种平台将CPU与并行处理器GPU整合到单个硅片中并共享内存，这是一项受诸如高清视频，面部识别和云工作量等高级功能驱动的硬件技术。Sumatra是OpenJDK中的一个项目，它的目的是将Java引领到异构平台中--针对那些高级应用与平台，硬件与软件专家们一同去修改JVM。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 接下来，Ramani讨论了Java在被称之为"下一代IT革命"嵌入式领域--"Internet"和M2M--中的近况，Java被认为是这一生态系统中的理想技术。上周，Oracle发布了Java ME Embedded 3.2(针对微控制器与低功耗器件)，以及Java Embedded Suite 7.0(一个基于Java SE 7的中间件组件)。Cinterion的战略与市场副总裁探讨了其公司在M2M方面对Java的使用，以及他们最新发布的EHS5，这是世界上最小的可接入3G的M2M模块，运行Java ME Embedded。Hansmaan解释道，Java为他们提供了针对诸多外围设备创建一种"易用的，可伸缩的，一致的，端对端的层"。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Royal Canadian Mint的首席账务官Marc Brule也探讨了JavaCard在其国家的MintChip电子现金技术中的绝妙应用--该技术可在智能手机，USB设备，计算机，平板电脑或云中进行部署。在这其中，Ramani鼓励开发者们下载并尝试最新的Java Embedded版本。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Fusion Middleware Development与Java EE开发的副总裁Cameron Purdy总结了企业级领域最新的开发与声明--Java EE 6中更好的开发者生产率(EE 7中还会更好)，平台、提供商之间，甚至云对云的移植性。Java EE 7 SDK的早期版本已能下载了--在GlassFish 4中--其中包含有WebSocket支持，更好的JSON支持，及其它。该版本的最终发布计划是在2013年4月。Nike的用户数字化技术高级总监Nicole Otto探究了其公司受企业级生态系统驱动的Java技术，该生态系统针对所有的体育用品，包括NikeFule加速计量腕带。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在展望Java EE 7时，Purdy提到了针对EE 8的NoSQL数据库功能，并发工具集(可能在EE 7中)，EE 7和EE 8中的一些Avatar项目，云上的多租期(multi-tenancy)，对SaaS应用的支持，及其它。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 最后，Rizvi介绍出海洋学家Robert Ballard博士，以及National Geographic的Explorer-in-Residence计划--这是Oracle与National Geographic Society慈善关系中的一部分，他们一同资助了K-12海洋科学教育与对话。Ballard因发现铁达尼号的残骸而广为人知，他提供了一段有趣的视频来概述了这种用于诸如深海探索的尖端技术。注意，在他早年，高带宽勘探意味着你必须进入潜水艇，然后"把你的脸贴在窗户上"。现在则是使用在远程进行操控的远程呈现技术--"我把我的Hercules船想像成一个纳美人。当潜入海中，我真地放飞了灵魂"。使用高带宽卫星链接，如此美妙的探索就能出现在智能手机，笔记本电脑或其它平台上了。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Ballard的团队会与该探索计划中的教育者们一同定期地为学校以及世界上188个国家提供实况与编程服务，这些最棒的技术将激发着下一代的科学家与探索者们！</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/389103.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2012-10-06 19:26 <a href="http://www.blogjava.net/jiangshachina/archive/2012/10/06/389103.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Duke's Choice Award 2012(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/10/03/388998.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 03 Oct 2012 15:08:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/10/03/388998.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/388998.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/10/03/388998.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/388998.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/388998.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><span style="font-size: 14pt;">Duke's Choice Award 2012</span></div><span style="font-size: 10pt;">本年度的<a href="https://blogs.oracle.com/javaone/entry/duke_s_choice_award_ceremony">Duke's Choice Award</a>已在JavaOne 2012大会中颁出，值得提及的是，Java之父James Gosling目前所工作的公司也获得了该项大奖。(2012.10.05最后更新)</span><br /><br /><span style="font-size: 10pt;">在周日晚上JavaOne主旨演讲之后，2012年度Duke's Choice Award获得者以及他们创造性的Java技术与Java社区贡献被授予了各自的荣誉。Java技术推广部门总监Sharat Chander出席了颁奖礼。在候选名单的提交与最终获奖者的选择方面都是社区直接参与的，这正体现了我们是如何在Java社区中促进对创新的发掘。</span><br /><br /><strong><span style="font-size: 10pt;"></span></strong><a href="http://hadoop.apache.org/"><strong><span style="font-size: 12pt;">Apache软件基金会</span></strong></a><br /><span style="font-size: 10pt;">Apache软件基金会的Hadoop项目由Java语言写成，它为跨计算机集群的分布式大数据集处理提供了一个框架，这个集群的规模从几个服务器到数千台机器不等。对大数据池的处理使机构能更好地理解并改进他们的业务。</span><br /><br /><a href="http://agrosense.org/"><strong><span style="font-size: 12pt;">AgroSense项目</span></strong></a><br /><span style="font-size: 10pt;">AgroSense是一个由Java和NetBeans平台构建的开源耕作信息管理系统，它的目标是改进耕作方法以养活这个饥饿的世界。AgroSense使农场主，农业综合企业，供应商及其他人能够开发模块化的应用，通过用底层地NetBeans框架，这些应用能方便地进行信息交换。</span><br /><br /><a href="http://jduchess.org/"><strong><span style="font-size: 12pt;">JDuchess</span></strong></a><br /><span style="font-size: 10pt;">与大多数Java用户组(JUG)关注特定地域不同，JDuchess关注于在世界范围的Java社区中培养女性的参与度。该组织在60个国家有超过500个成员，它为女性进行相互联系并参与到Java社区的各个方面提供了一个平台。</span><br /><br /><a href="http://jelastic.com/"><strong><span style="font-size: 12pt;">Jelastic, Inc.</span></strong></a><br /><span style="font-size: 10pt;">向现有Java应用移植到云中是一项令人望而生畏的工作，但Jelastic开始这么干了。Jelastic提供了第一个全Java的平台即服务(PaaS)应用，它允许已有的Java应用在不需要修改或锁定代码的情况就能部署到云中。</span><br /><br /><a href="http://www.liquidr.com/"><strong><span style="font-size: 12pt;">Liquid Robotics</span></strong></a><br /><span style="font-size: 10pt;">Liquid Robotics是一个海洋数据服务提供商，它的Wave Glider技术能够为政府，科研和商业应用收集世界海洋的信息。特别之处是"Java之父"James Gosling是该公司的首席软件架构师。</span><br /><br /><a href="http://londonjavacommunity.wordpress.com/tag/london-java-community/"><strong><span style="font-size: 12pt;">London Java Community</span></strong></a><br /><span style="font-size: 10pt;">今年第二个获得Duke's Choice Award的用户组是伦敦Java社区(LJC)，它的用户在OpenJDK，Java社区进程(JCP)及全球其它Java社区中的贡献使其获得该奖项。</span><br /><br /><a href="http://www.nato.int/cps/en/natolive/index.htm"><strong><span style="font-size: 12pt;">NATO</span></strong></a><br /><span style="font-size: 10pt;">前所未有的社区选择奖给了NATO使用的MASE Integrated Console Environment(MICE)。由Java和NetBeans平台构建的MICE为指挥防空与战场空间处理提供了一个高性能的可视化环境。</span><br /><br /><strong><span style="font-size: 12pt;"></span></strong><a href="http://www.parleys.com/"><strong><span style="font-size: 12pt;">Parleys.com</span></strong></a><br /><span style="font-size: 10pt;">位于比利时布鲁塞尔的E-learning方面的专家Parleys.com使用Java技术将在线班级与完全的IT会议带入到桌面电脑，笔记本电脑，平板电脑及移动设备中。Parleys.com为超过80万独立访问者主持了超过1700场会议--包括Devoxx和JavaOne。</span><br /><br /><a href="http://www.studentnokiadeveloper.com/"><strong><span style="font-size: 12pt;">Student Nokia Developer Group</span></strong></a><br /><span style="font-size: 10pt;">今年的学生奖获得者Ram Kashyap,是Nokia Student Network的创始人与总裁，在今年3-4月期<a href="http://www.oraclejavamagazine-digital.com/javamagazine/20120304">Java Magazine</a>中的"新Java开发者"中有介绍。从创办该项目以来，Ram维护着一个来自于印度班加罗尔PES技术学院的大受欢迎的应用，该应用工作于一个Java移动环境并在Java ME方面对学生们进行培训。</span><br /><a href="http://www.unhcr.org/cgi-bin/texis/vtx/home"><strong><br /><span style="font-size: 12pt;">United Nations High Commissioner for Refugees</span></strong></a><br /><span style="font-size: 10pt;">联合国难民高级事务专员(UNHCR)处在世界各地从内战到自然灾害等危机的前沿。为了有助于促进人道主义救济使命，UNHCR已经开发了一个基于NetBeans平台的轻客户端Java应用。其中的Level One注册工具使UNHCR能够收集关于难民的数量，以及他们的饮用水，食品，住房，健康和其它必要信息，同时与多个来源的地理信息结合在一起，这使得UNHCR能够发出该地区所需的适当类型与数量的援助。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/388998.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2012-10-03 23:08 <a href="http://www.blogjava.net/jiangshachina/archive/2012/10/03/388998.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>两年经验不会使你成为"资深"(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/06/30/381893.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 30 Jun 2012 15:47:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/06/30/381893.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/381893.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/06/30/381893.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/381893.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/381893.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">两年经验不会使你成为"资深"<br /></strong></div>本文是<a href="http://java.dzone.com/">JavaLobby</a>的一篇<a href="http://java.dzone.com/articles/two-years-experience-doesn%E2%80%99t">博文</a>，作者是Eclipse基金会的技术传教士，以"资深"人士的口吻对非"资深"求职者的一点儿忠告。文章很短，道理很浅。(2012.07.05最后更新)<br />&nbsp;&nbsp;&nbsp; 可能除了在高中，两年工作经验并不会使你成为"资深"人士。我不是以消极方式来说这个事儿，而是以使试图帮助你的方式来说它。<br />&nbsp;&nbsp;&nbsp; 在我二十多年的职业生涯中，我工作过的公司的数量相对较少，反正，与软件行业的标准情况相比是少的。在我的每份工作中，我都会牵扯到招聘过程。在大多数情况下，我会参与到招聘的全过程：发布职位介绍，整理求职信与简历，面试，以及推荐聘请。<br />&nbsp;&nbsp;&nbsp; 我的观点是，筛选求职信和简历是最困难的部分。曾经有时候，针对一个职位我会收到超过的1000份的申请。通常，第一步是筛选这些申请，列出恰当数量(比如说十几个左右)的人，使你能够与他们在电话中谈谈。你会希望进一步缩减这份清单到很少量(比如四或五)的人，使你能够进行一个面对面的面试。你不可能真正地从简历或求职信中了解到某个人，雇主只能用这些简历来挑出他们所能了解到的人。有时候。从上千份申请中筛选出十几个左右的候选者需求一些技巧。<br />&nbsp;&nbsp;&nbsp; 我倾向于在申请者身上发现两样东西：他们的具体技能，以及他们是否关注细节。我不关心简历是否用钴蓝纸打印；我不关心简历是否使用了花哨的字体(然而如果简历选用的字体使我很难看懂，那我会关注一下)；我不关心简历是否对折整齐了；我不关心你是否赢得了奥运金牌。准确地说，我确会关心奥运金牌：那确实很酷，但它还不足以使你进入下一轮。<br />&nbsp;&nbsp;&nbsp; 求职信和简历必须强调你的相关技能。我希望工作申请能列出从事该工作所要求的大部分技能。<br />&nbsp;&nbsp;&nbsp; 求职信和简历应该语法正确，并且所有的单词也应该拼写正确。我能阅读语法和拼写正确的英文，以及美式英文，你挑一个吧。<br />&nbsp;&nbsp;&nbsp; 关于这个话题的细节，让我们回到这篇文章的题目上来：两年经验不会使你成为"资深"。不要告诉我，你两年前大学毕业，现在已经作为某类"资深"人士在这个领域工作了。拥有两年的工作经验，以及一点儿运气，你可能最终只是"领导"开发员；但你并不资深。在你能称自己资深之前，你还需要更多年的实际行业经验。<br />&nbsp;&nbsp;&nbsp; 如果你是这个行业中刚起步的年轻人，我给你这样的建议：不要吹捧自己，真实地表现自己，关注细节，对你正在申请的公司作一点儿研究。软件行业重视潜力。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/381893.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2012-06-30 23:47 <a href="http://www.blogjava.net/jiangshachina/archive/2012/06/30/381893.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>推动Java前进--Java Strategy Keynote JavaOne 2011(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Fri, 07 Oct 2011 03:51:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/360101.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/360101.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/360101.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">推动Java前进--Java Strategy Keynote JavaOne 2011</strong></div>每年的JavaOne都会发布关于Java未来发展的计划或愿景，在今年的Java Strategy Keynote中，Oracle则向大家描述了将如何推动Java继续前进。<a href="http://blogs.oracle.com/javaone/entry/moving_java_forward_java_strategy">本文</a>是JavaOne2011的官方博文，概述了Java Strategy Keynote中提及的重要内容。(2011.10.07最后更新)<br /><br />&nbsp;&nbsp;&nbsp; 周四的战略主旨演讲展示了Oracle在对待Java投资与创新方面的长期愿景。该议题包罗广泛，涉及技术，合作伙伴，宣言和路线图--从移动与手持设备，到桌面应用，再到云。<br />&nbsp;&nbsp;&nbsp; 早上，先由David Ward开始，他是Juniper Networks平台系统部门的CTO与首席架构师。Ward详述了在云时代开发者们认知并利用可编程的"由软件定义的网络"时代的必要性。Ward探究了直接对网络进行编程的应用接口，告之了它最得意的功能，使用网络设备确定实际的位置/拓扑，终端设备的功能，实时应用的要求，以及其它方面。总之，这些接口定义了一个双向的交互与可编程特性。Ward说道，"来源于网络的是对其拓扑的实时理解，而从应用空间到网络中去的是将通信引导到高效途径中"。<br /><br /><strong><span style="font-size: 12pt;">推动Java前进：三大支柱</span></strong><br />&nbsp;&nbsp;&nbsp; 随后，Hasan Rizvi，Oracle Fusion中间件与Java部门的高级副总裁，简要探讨了"推动Java前进"的三大支柱：正确的技术，Java社区，以及Oracle对Java的领导/管理。在该环节中，Rizvi提供了一个近期里程碑的记录，包括在今年夏天发布的JDK 7，正在开发中的(且由社区深度参与的)Java EE 7，JavaFX 2.0，以及刚刚针对Mac OS X发布的JDK 7预览版。他也提到了IBM，Apple以及SAP成为OpenJDK社区的成员，当然还有其它成员。<br />&nbsp;&nbsp;&nbsp; 然后，Intel的Java技术主管Jason Gartner，RedHat的高级工程主管Mark Little博士，以及Intel的软件工程主管Steven Chin也走上台，加入到Rizvi的演讲中。Gartner宣布，上周发布了<a href="http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html">IBM平台上的Java 7</a>，他也提到，尽管Oracle与IBM之间存在竞争关系，但Java平台基于标准的合作本质是它最强大的力量之一。他还稍稍讨论了Java EE 6的开放式开发成果，以及这对JBoss产品的益处。Chin讨论了Oracle与Intel之间的合作，在过去4年中，这一合作致使Java的性能在Intel硬件中提高了14倍。<br /><br /><strong><span style="font-size: 12pt;">延伸Java的触角</span></strong><br />&nbsp;&nbsp;&nbsp; Adam Messinger，负责Java SE与Java ME产品线的副总裁，讨论了延伸Java的触角，以跨越新的应用模型及新的设备类型的目的。一种方法就是通过模块化，这是Java SE 8中的计划。同时，Java 7的Coin项目与Java 8的Lambda项目致力于更高的开发者生产效率。如下的路线图有清晰的描述：<br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2011/slide_15_large_formatted.gif" height="278" width="643" /><br /><br /><img alt="" src="http://www.blogjava.net/images/blogjava_net/jiangshachina/javaone2011/slide_16_large_formatted.gif" height="278" width="643" /><br /><br />&nbsp;&nbsp;&nbsp; Rob Benso，Twitter的运行时系统主管，随后也与Messinger同台演讲。Benson详述了Twitter在需要一个成熟且有高伸缩能力的技术时，是如何应用Java的。"我们每天要处理大约2.3亿条推特，我们的流API每天要推送大约6TB的数据，并且我们的公共API每天要服务大约13亿个请求。所以我们期望中的运行时环境要能够处理当前及未来的通信量。"Twitter选择了Java，是由于它有一个庞大且充满活力的开源社区。Twitter当场宣布，他们已经加入了OpenJDK，同样也加入了JCP。<br />&nbsp;&nbsp;&nbsp; Messinger重申推进JavaFX在战略上的重要性，JavaFX是Oracle首要的RIA开发环境，它包含对已有Swing应用的交互，以及能进行可视化开发的JavaFX Scene Builder工具。随后，Messinger的同事Nandini Ramani提供了一个实验性的JavaFX游戏的原型，该原型能运行在多种便携设备上--包括Windows，Linux，甚至是Apple iPad。<br />&nbsp;&nbsp;&nbsp; Messinger还宣布了Oracle对JavaFX的开源计划，首先开源的是组件，然后是框架的其它部分。只要OpenJDK社区提出要求，这一开源计划就会执行。另外，他们还计划对JavaFX进行标准化，使它成为Java SE的一个标准组成，这可能要到Java 8之后了。<br /><br /><strong style="font-size: 12pt;">Java移动与嵌入式的三大目标</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; 后来，Messinger谈到Oracle针对Java移动与嵌入式体系的三大目标--在Java SE设备与Java ME设备之间起到桥梁作用(同步CLDC与JDK的版本/聚合CDC与嵌入式Java SE应用)，完整地涵盖嵌入式垂直市场，并在运行时环境与工具方面提供深度的内容与服务整合。这必将导致Java Card技术应用于极小设备，Java ME应用于小的嵌入式环境，而Java SE应用于更大的嵌入式市场。<br />&nbsp;&nbsp;&nbsp; 在真正的嵌入式领域中，ARM的执行副总裁Lance Howarth探讨了"无处不在的计算"的将来。作为制造商随处都使用的32位RISC微处理器(基于ARM的设备已经出厂了610万台)，Howarth预测对于智能计算设备，我们很少意识到，也很少见到把Java作为实现这一设想的关键。对之后，移动手机应用商店GetJar的CEO Ilya Lars探讨他们对移动领域的愿景，以及他们对Java开放标准的提案。<br /><br /><strong style="font-size: 12pt;">Java EE路线图</strong><br style="font-size: 12pt;" />&nbsp;&nbsp;&nbsp; Oracle开发部门的副总裁Cameron Purdy详述了Java EE路线图。Purdy提到Java EE是"企业级应用所依赖的唯一标准开发平台"，这些应用可以跨越不同的社区，不同的软件提供商，以及不同的开源实现。他还讲到Java EE当前是企业级开发者的首选，它已经被下载4000万次了。Java EE的目标是使EE易于开发--在从事往常复杂而费解的任务时，EE会使Java开发者们感到更为自然，同时为下一代企业应用建立了一个开放的，由社区驱动的，且基于标准的平台。他探讨了很多Java EE 7的独创特性，如多租用(multi-tenancy)，按需扩容(capacity on demand)，以及自动提供(auto-provisioning)，这些特性使高级云应用及其平台成为一种服务。<br />&nbsp;&nbsp;&nbsp; 随后，ESPN的Sean Comerford与Purdy讨论了在ESPN.com在当前的企业需求中对Java EE的选择。"Glassfish给了我们相同的性能，但与其它可选技术相比，它还具备高得多的可伸缩性。在处理每秒3000次请求的场景中，对我们的产品部署进行了测试。"对于ESPN，游戏的规则就是可伸缩性，还伴随着未来在任何地方、任何设备上提供服务的潜在可能。<br />&nbsp;&nbsp;&nbsp; 然后，Adam Messinger以对Avatar项目的介绍结束了本场会议。Avatar项目是Oracle针对动态富客户端的混合编程模型，它在浏览器中集成HTML 5作为UI，使Java应用作为控制器与模型，而Java EE 7则在后台的云中--这统一了Java ME，Java SE和Java EE。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/360101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-10-07 11:51 <a href="http://www.blogjava.net/jiangshachina/archive/2011/10/07/360101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Duke's Choice Award 2011(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 04 Oct 2011 16:13:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/360024.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/360024.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/360024.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">Duke's Choice Award 2011</strong></div>JavaOne 2011还在进行中，前日(2011.10.03)，本年度的Duke's Choice Award已新鲜出炉。<a href="http://blogs.oracle.com/javaone/entry/and_the_winners_are_the">本文</a>是JavaOne的官方博客，记录了本次全部获奖应用，大家可以看看这些应用有何高明之处。(2011.10.05最后更新)<br /><br />10月2号，星期日的晚上，在JavaOne Open House，Oracle欢迎并祝贺了2011年度Duke's Choice Award的获得者们。这些睿智的开发者们证明了由Java应用所创造的极致创新能力。<br />获奖者是...<br />1. <a href="http://community.jboss.org/wiki/Arquillian">Arquillian</a>--该项目使基于Java的应用的集成测试得到了简化。它为开发者们提供了一种在远程或内嵌的容器中，或与容器交互的客户端中，测试应用程序的简单机制。<br /><br />2. dooApp--<a href="http://www.dooapp.com/en">Infiltrea</a>是一个使用JavaFX与Java SE平台构建的点对点解决方案，该方案是为环保建筑工程师而设计的，他们的工作是测量建筑物的空气密度。<br /><br />3. <a href="http://www.inductiveautomation.com/">Inductive Automation</a>--Ignition是一个基于Java的Web应用，它包含一个商业记分卡，该卡使用一个中央Web服务器将不同的生产机器集成到一起。该系统涵盖了人机界面(HMI)，管理控制与数据采集(SCADA)，以及生成执行系统(MES)，这使得能够快速地进行项目开发与部署。<br /><br />4. <a href="http://download.oracle.com/javaee/6/firstcup/doc/gcrky.html">jHome</a>--一个用于Glassfish/Java EE的开源的完整家用自动化API，它使开发者能够在家中控制任何器具，例如电灯，LED灯和咖啡机。<br /><br />5. JFrog--它的<a href="http://www.jfrog.com/products.php">Artifactory</a>这是世界上第一个双仓库管理器。它基于Java内容仓库(JCR)API规范构建而成，能够帮助开发者改变通过传递高并发和不匹配的数据完成性来构建并管理软件模块的途径。<br /><br />6. <a href="http://www.zeroturnaround.com/jrebel/">JRebel</a>--这是一个JVM插件，它使得Java开发者能够即时地查看应用中哪些程序发生了改变。它允许开发者跳过构建与重布署阶段，它已经阻止了3900万次重部署了。<br /><br />7. LMAX--<a href="http://code.google.com/p/disruptor/">LMAX Disruptor</a>是一个多线程，开源的高并发编程框架，它是为高性能且低延迟事务处理而设计的。作为LMAX的Java交易系统，LMAX Disruptor替代了java.util.concurrent.ArrayBlockingQueue，而且比它快80倍以上。<br /><br />8. <a href="http://www.rockwellautomation.com/rockwellsoftware/">Rockwell Automation</a>--这是一个开创了下一代，基于Java的HMI设备产品线，该设置将允许自动地对工厂生产线进行数据通信与交换。<br /><br />9. <a href="http://sodbeans.sourceforge.net/">Sodbeans</a>--该项目是一个开源的，基于NetBeans的模块套件，它是为在现代编程环境中改善盲人与外界交流的能力而设计的。这个NetBeans模块套件包含一个新的易用的编程语言，一个使NetBeans兼容多平台屏幕阅读器的文本语音转换引擎，一组为让盲人易于编程而设计的语言特性。<br /><br />10. <a href="http://www.jboss.org/netty">Netty</a>--该项目是一个基于Java的NIO C/S框架，它使得能够快速且方便地进行网络程序的开发。它极大地简化了网络编程，例如TCP和UDP的套接字服务器。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/360024.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-10-05 00:13 <a href="http://www.blogjava.net/jiangshachina/archive/2011/10/05/360024.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IBM Java SDK 7(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 04 Oct 2011 08:34:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/360010.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/360010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/360010.html</trackback:ping><description><![CDATA[<div><div align="center"><strong style="font-size: 14pt;">IBM Java SDK 7<br /></strong></div>昨天(2011.10.03)，IBM发布了它的Java SDK 7，目前仅支持AIX和Linux平台。<a href="http://www-01.ibm.com/support/docview.wss?uid=swg21516002">本文</a>是IBM上的一篇新闻，大家可以看看它有哪些新特性或变化。(2011.10.04最后更新)<br /><br />IBM Java SDK 7现在已经可用了，目前支持AIX和Linux平台。<br />在developerWorks中下载SDK，请见<a href="http://www.ibm.com/developerworks/java/jdk/index.html">http://www.ibm.com/developerworks/java/jdk/index.html</a>。<br /><br /><strong style="font-size: 12pt;">新特性</strong><br style="font-size: 12pt;" />该版本含有许多新的改进与特性，包括：<br />1. Coin项目(JSR 334)的语言改进特性，例如：支持字符串的switch语句块，钻石操作符，以及自动的资源管理。<br />2. NIO 2(JSR203)的异步I/O功能，扩展的文件系统属性，以及文件系统通知。<br />3. 使用新fork/join框架的java.util.concurrent新功能。<br />4. 一个新的平衡性垃圾回收(GC)策略，它会使于大型堆中的暂停时间短暂且保持一致。<br />5. 更详细且更有效的GC详情格式。<br />6. 重要的诊断提升，包括对javacore.txt内容(极值，本地栈，以及本地内存使用)的改进。<br />7. 对跟踪能力的改进，这一改进允许在任一跟踪点捕获Java栈路径。<br />8. 改进了将错误信息记录到操作系统日志，如Linux中的syslog，的功能。<br />9. 改进了对共享类缓存的支持，例如：附加内容，对缓存缓崩溃更好的诊断能力，查找并销毁缓存的可编程接口，能更好地控制持久化缓存文件许可，以及对展示缓存内容的更多的控制。<br /><br /><strong><span style="font-size: 12pt;">实时评估技术</span></strong><br />IBM Java SDK 7还包括一个实时增量GC策略(-Xgcpolicy:metronome)，Metronome，但目前只作评估之用。虽然该策略还不可用于Java 7许可协议，但在<a href="http://www.ibm.com/software/webservers/realtime/">WebSphere Real Time</a>产品中已获支持。<br /><br /><strong style="font-size: 12pt;">重要的改变</strong><br style="font-size: 12pt;" />1. 默认的GC策略变为分代并发垃圾收集策略(-Xgcpolicy:gencon)。<br />2. 如果安装了受支持的AIX版本，AIX会默认使用持久化的共享类缓存，以替换非持久化缓存。<br />3. 俄罗斯提议在10月份对夏令时进行修改，如果该提议被采纳，将需要更新到Java 7的时区数据。受此变化影响的客户可以使用IBM的<a href="http://dwmaster.raleigh.ibm.com/developerworks/java/jdk/dst/jtzu.html">Java Time Zone Update工具(JTZU)</a>来应用这一更新。关于使用该工具进行时区更新的信息，请参见<a href="http://www.ibm.com/developerworks/java/jdk/dst/olson_table.html">http://www.ibm.com/developerworks/java/jdk/dst/olson_table.html</a>。<br /><br /><strong><span style="font-size: 12pt;">更多信息</span></strong><br />针对IBM Java 7 SDK和JRE的用户文档可到<a href="http://publib.boulder.ibm.com/infocenter/java7sdk/v7r0/index.jsp">IBM信息中心</a>获取。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/360010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-10-04 16:34 <a href="http://www.blogjava.net/jiangshachina/archive/2011/10/04/360010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java SE 7发布：2006年12月以来的首个主版本升级(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 30 Jul 2011 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/355420.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/355420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/355420.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">Java SE 7发布：2006年12月以来的首个主版本升级</span></strong></div><span style="font-size: 10pt;">在经过漫长地等待之后，Java SE 7终于发布了。<a href="http://www.infoq.com/news/2011/07/javase7-ga">Info的这篇文章</a>总结了JDK 7的关键新特性，希望对大家了解JDK有帮助。(2011.07.30最后更新)</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 从今天开始就可以使用Java 7了，这是Oracle收购Sun之后Java平台发布的第一个版本。该版本包含一系列很小但很受欢迎的语言变化，含有一个新的文件API及Fork/Join框架，改进了JVM对动态语言的支持。 </span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; Java 7的语言级变化已成为Coin项目(<a href="http://jcp.org/en/jsr/summary?id=334">JSR 334</a>)的一部分，该项目是<a href="http://jcp.org/en/jsr/summary?id=201">JSR 201</a>的后继者，它被设计成使用一组能改善生产率的细小变化来提升Java语言。特别值得注意地是对"try-with-resources"的介绍。这是对C#的using语句的模仿，但是基于try语句的形式。结果，using语句只能处理单一资源，而try-with-resources能够在给定语句块的范围内处理多个资源。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 有两个针对异常处理的变化。第一，多个异常类型能够由单个try语句块处理。扩展了try语句中catch子句的语法，使它能处理一系列的异常类型，这些异常类型由"OR"操作符"|"分隔开，这将用于异常参数的声明处。第二，如果一个异常参数未被修改，且未在cactch语句块中被重新抛出，就不必把该异常加到方法签名中。</span><br /><span style="font-size: 10pt;">其它的语言级变化还包括：</span><br /><span style="font-size: 10pt;">1. String类型可用于switch语句</span><br /><span style="font-size: 10pt;">2. 支持二进制常量，且能在数字中使用下划线进行分隔，例如，long creditCardNumber = 1234_5678_9012_3456L</span><br /><span style="font-size: 10pt;">3. 简化的可变长参数方法的调用：当包含可变长参数的方法使用了不确定的数组类型时，编译器会报出警告。现在将该警告从方法调用处移到了方法声明处。</span><br /><span style="font-size: 10pt;">4. 改进了泛型实例创建时的类型推导(&lt; &gt;，即钻石符)：在创建类实例时使用受限制的类型推导，以便显式地为构造器声明参数类型，但从上下文可以确定这些类型，那么可用一个空的类型参数设置去替代这一声明。所以，为了替代如下写法：</span><br /><span style="font-size: 10pt;">Map&lt;String, List&lt;String&gt;&gt; anagrams = new HashMap&lt;String, List&lt;String&gt;&gt;();</span><br /><span style="font-size: 10pt;">可以写成：</span><br /><span style="font-size: 10pt;">Map&lt;String, List&lt;String&gt;&gt; anagrams = new HashMap&lt;&gt;();</span><br /><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 已经介绍过两个主要的新API。第一个是<a href="http://jcp.org/en/jsr/summary?id=203">JSR 203</a>，这个API的加入是为了文件系统的访问，可伸缩的异步I/O操作，Socket-Channel绑定与配置，以及多播数据报。特别能引起企业级开发者兴趣的是真实的异步I/O API。对于那些会跨许多连接且要求低延时、高吞吐的高级服务器端应用，该API显得尤为重要。作为加入Java的最后一个文件系统API，JSR 203还支持某些操作系统的特殊功能。例如，你可以为那些支持符合链接的系统创建这种链接。因此，JSR 203有些争议，它并不严格遵循"编写一次，随处运行"这一原则，尽管它提供了一组能运行在所有平台上的通用API，但它也提供了某些平台的某些特定功能。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 第二个新API就是Fork/Join框架(它是<a href="http://jcp.org/en/jsr/summary?id=166">JSR 166</a>的一部分)，原计划是放到Java 5中的。它为开发者提供了一种将问题分解成任务的机制。这些任务可在任意数量的多核处理器中并发执行。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在其它方面，平台寻求新的网络与安全特性，并扩展了对国际化的支持，包括对Unicde 6.0的支持。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 最后，Java SE 7还引入自Java起始以来最新的字节码结构，<a href="http://jcp.org/en/jsr/detail?id=292">InvokeDynamic</a>关键字。InvokeDynamic加入了多于一个的调用模式，以及多于一个的链接模式，这样就能基于用户定义的规范进行编程。它的本意是在缺少静态类型信息时能高效且灵活地执行方法调用，这就使得运行在JVM上的动态语言，如JRuby和Jython，的性能得到实质性地提升。</span><br />&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在欢迎这些特性的同时，从许多方面看来，对于该版本最重要的事情，是它真的终于发出来了。在Sun和Apache软件基金会之间长期存在的分歧，可能还有Sun在它最后的日子中的领导力与资源分配方面的问题，导致了这次版本间不同寻常的长期断层。就如Mark Reinhold在最近的一次<a href="http://medianetwork.oracle.com/media/show/16796">访谈</a>中所评论的：</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; </span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><img src="http://www.blogjava.net/Images/dot.gif" alt="" />由于商业与政治方面的原因，Java在一段时间内进行了冬眠模式。但我们又回来了，并且将要发布版本7。它不是一个革命性的版本，只是一个改进的版本，但它里面确实有一些好东西。</span></div><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开发者们对JDK 7版入门的兴趣，就会撬动对<a href="http://netbeans.org/community/releases/70/">NetBeans 7.0</a>与<a href="http://www.jetbrains.com/idea/">IntelliJ IEDA 10.5</a>的应用，它们都支持Java SE 7平台的最新特性。<a href="http://www.eclipse.org/indigo/">Eclipse Indigo</a>的<a href="http://wiki.eclipse.org/JDT/Eclipse_Java_7_Support_%28BETA%29">beta版也已支持Java 7</a>，Oracle的JDeveloper计划在今年晚些时候发布一个版本去支持JDK 7。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/355420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-07-30 22:23 <a href="http://www.blogjava.net/jiangshachina/archive/2011/07/30/355420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>James Gosling现在是Google人了(泽)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 29 Mar 2011 16:08:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/347244.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/347244.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/347244.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">James Gosling现在是Google人了</span></span></strong><br />
</div>
<span style="font-size: 10pt;">Java之父<a href="http://nighthacks.com/roller/jag/">James Gosling</a>已进入Google工作了，JavaLobby社区的一篇<a href="http://java.dzone.com/news/james-gosling-now-googler">文章</a>对该事件进行了评论，大家也可以发表自己的看法。(2011.03.30最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 人们已经问了几个月的问题--"Google会是新的Sun吗？"当然，你仍然可以争辩道这个答案为"否"，但它刚刚变成更为困难的争论了，因为James Gosling<a href="http://nighthacks.com/roller/jag/entry/next_step_on_the_road">刚刚告诉我们</a>，他开始了在Google的第一天工作。<br />
&nbsp;&nbsp;&nbsp; 这份简短声明今晨展示在Gosling的博客中，它使Java世界完全震惊了：<br />
&nbsp;&nbsp;&nbsp; "今天，我开始了在Google的工作。人生中最棘手的事情之一就是作出抉择。我有过一段困难的时光，在这段时间里我拒绝了许多其它极好的机会。我发现很奇怪的是，在这段时间里，我<a href="http://www.bartleby.com/119/1.html">游走了更多的地方</a>，但看起来这是有着极大影响且十分有趣的事情。"<br />
&nbsp;&nbsp;&nbsp; 还没有关于James所从事具体工作的消息，但如果这份工作邀请与Oracle针对Android的诉讼有关，我一点儿也不会感意外。但我可以肯定，一大堆公司曾经向Gosling大献殷勤，希望Gosling能加入他们的行列。Gosling在去年处于失业状态不太可能是一个巧合。可能是他处于与Oracle的一年非竞业合同的限制之下。<br />
&nbsp;&nbsp;&nbsp; 虽然Oracle可能拥有Gosling在Sun时曾经开发出来的所有一切，但还有一种可能的途径能使他帮助Google摆脱与Oracle的司法争吵。他的专利之一(RE38,104)是Oracle当前专权侵权诉讼的一部分。当然，我并不是说Google不想要Gosling丰富的程序设计专业知识以及创造精神。我可以肯定，Google能理解到它不是仅仅要把Gosling当作专利保护伞而以--但是无法保证的是，Gosling确实能扭转来自于Oracle的侵权主张。<br />
&nbsp;&nbsp;&nbsp; 这一变化会让一些人感到惊讶，因为当Android首次出现时，Gosling对Google进行了<a href="http://www.dzone.com/links/r/the_shit_finally_hits_the_fan_gosling_on_oracle_v.html">批评</a>。他也预测到，Android在维持与所以使用该开源软件的手机厂商关系方面存在着问题。<br />
&nbsp;&nbsp;&nbsp; 若有与这桩雇佣事情有关的任何消息出现，我们会让您知道的。<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/347244.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-03-30 00:08 <a href="http://www.blogjava.net/jiangshachina/archive/2011/03/30/347244.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>未来的Java开发者(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/03/13/346151.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 13 Mar 2011 10:22:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/03/13/346151.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/346151.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/03/13/346151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/346151.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/346151.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">未来的Java开发者</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是较早前发表于<a href="http://www.java.net">java.net</a>上的一篇<a href="http://www.java.net/blog/brunos/archive/2011/03/02/future-java-developer">博文</a>，它讨论了软件开发者们，特别是Java开发者们，具有哪些优势，现在应该为未来作哪些准备工作，希望对所有软件开发者们能有所提示。(2011.03.13最后更新)<br />
<br />
<a href="http://twitter.com/fabianenardon">Fabiane Nardon</a>，Duke Award获得者与Java Champion，<a href="http://nbguru.blogspot.com/">Sven Reimers</a>，Duke Award获得者与NetBeans梦之队成员，和我一起讨论了<a href="http://jav.mn/jfokus11">未来的Java开发者</a>，我已经发表了这些谈话。最近的谈话是在<a href="http://jav.mn/javaonebr09">JavaOne Brasil</a>和<a href="http://www.jfokus.se/">JFokus</a>。不可否认，我们不是特有远见的人，今天我们所谈到的"未来"仅凭经验罢了。虽然在谈话过程中，我们作出了一些特别的预测，但我们真的不想试着去预言太远的将来，也不愿争论Java技术的未来。我们的想法只是看看开发者们，特别是使用Java的开发者们，现在能做些什么，以为他们自己的未来作些准备。此处，我想展示谈话中的一些要点。<br />
&nbsp;&nbsp;&nbsp; 软件开发者一直都有一个独一无二的机会，这就是Malcolm Gladwell在他的书<a href="http://en.wikipedia.org/wiki/Outliers_%28book%29">Outliers</a>中所说的<a href="http://jav.mn/meanwrk">有意义的工作</a>：工作是自我管理的。工作是复杂的，它会占据你的思想。另外，工作是努力与回报之间的联系--<a href="http://www.bnet.com/videos/malcolm-gladwell-meaningful-work-through-passion-not-genius/246904">付出了，自然有回报</a>。<br />
&nbsp;&nbsp;&nbsp; Gladwell所说的关于"有意义的工作"的一个要点就是花时间使某人成为大师。他表述道，根据对不同领域--音乐，计算机，法律，农业--的研究，要在特定领域取得成功，差不多要花10000个小时在做有意义的工作上。Gladwell表示道，那些成功投入大量时间的人，以及那些成功的人，肯定都会投入大量的时间。Gladwell也讨论到并没有所谓的"天生"牛人，简言之，传统谚语"1%的灵感和99%的努力"不仅是真理，而且也是必然的。<br />
&nbsp;&nbsp;&nbsp; 为未来做准备意味着，现在投入足够多的时间会使你在未来成为更优秀和更重要的人。基于这种思维，软件开发给予我们一些有趣的好处，由于缺少更好的词汇，我将其称之为自由。一些是旧有的自由，在软件刚开始开发时就存在了，另一些则是新近才出现的，幸运地是，我们恰逢其时，正好能从中受益。<br />
<br />
<strong><span style="font-size: 12pt;">想像之自由</span></strong><br />
</span>
<div align="center"><span style="font-size: 10pt;"><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/future_java_developer/future_java_developer_01.jpg" alt="" border="0" /></span><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 如Dilber卡通画所说："试着使这样一种观念深入你的脑髓：软件能够做任何你设计让它做的事情"。你的想像力是有限的。缺少约束使软件的某些方面极为强大，也极其复杂。作为一种推论，你要深深领会的是软件开发困难的，它不会因为出现一种新语言，新框架或新工具而变得简单。在1975年，Edsger Dijkstra写到"程序设计是数学应用各分支中最困难的分支之一；拙劣的数学家最好保持其纯粹数学家的本色"。知道它到底有多难吗？开发者们怀着激情来从事困难的工作，而他们自由的想像力则总是走在前面。<br />
<br />
<strong><span style="font-size: 12pt;">随处运行之自由</span></strong><br />
&nbsp;&nbsp;&nbsp; Java并不是第一个提出了"随处运行"的思想，却是第一个普及了该思想的技术。这并不是一个仅有利于Java的技术：自从在二十世纪九十业界将开发者从禁锢中解放出来，开发者认识到他们能够编写运行在多个环境上的软件。今天，所有的开发技术会以这样或那样的方式尝试着向你提供这种自由。当然，也许不是全部...但不应该是我们"不知道"的：不要把你自己绑定在单个技术提供者或平台上。挑选那些使你有充足机会在多个环境上进行实验的技术，这是使你在现在就能自由地对在未来可能有价值的技术进行实验的唯一方法。Java并不是第一个提出了"随处运行"的思想，却是第一个普及了该思想的技术。这并不是一个仅有利于Java的技术：自从在二十世纪九十业界将开发者从禁锢中解放出来，开发者认识到他们能够编写运行在多个环境上的软件。今天，所有的开发技术会以这样或那样的方式尝试着向你提供这种自由。当然，也许不是全部...但不应该是我们"不知道"的：不要把你自己绑定在单个技术提供者或平台上。挑选那些使你有充足机会在多个环境上进行实验的技术，这是使你在现在就能自由地对在未来可能有价值的技术进行实验的唯一方法。<br />
&nbsp;&nbsp;&nbsp; 随处运行还有另一个同样重要的方面：一旦有许多程序能够在不同的设备上运行，制造商就能更容易地创建新设备。我们在Android中看到了这种趋势：通过放大开发者的才智和工具，并允许(至少是在一定程度上)开发者作用于不同的设备，Android创建了一个强大的且有众多提供商参与的应用程序市场。使用大多数面向多平台的开发技术，未来将会带来更多的设备，机会将会为那些执着于随处运行之自由的开发者敞开。<br />
<strong><span style="font-size: 12pt;"><br />
学习与构建之自由</span></strong><br />
</span>
<div align="center"><span style="font-size: 10pt;"><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/future_java_developer/future_java_developer_02.jpg" alt="" border="0" /></span><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 开源是一个改变世界的现象，对于开发者而言也可能是最重要的事情了。向Bart学习，重复说"开源有利于我，我将拥护它"一万遍。就这么干，你行的。如果你计划在软件开发上花上大量时间，你就需要以软件为业，你会怀着激情与热情在这一工作上。如果你想为未来作好准备，你需要能够学习由其他人已创造好的软件，与其他人一道创造软件，还要让其他人在你的工作基础之上创造软件。如果未来是在你所参与创造的事物的基础之上而创造的，那么当未来发生时，你就处在正确的位置了，你将创造未来。<br />
&nbsp;&nbsp;&nbsp; 对于公司，政府和用户而言，开源还是许多其它的好处。但没有人比开发者们受益更多。越快认识到这一点，你就能越快地知道自由地进行学习和创造的价值。<br />
<strong><span style="font-size: 12pt;"><br />
随处(与任何人一块儿)工作之自由</span></strong><br />
</span>
<div align="center"><span style="font-size: 10pt;"><img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/future_java_developer/future_java_developer_03.png" alt="" border="0" /></span><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 随处工作是一种<a href="http://www.fourhourworkweek.com/blog/">风格</a>，随着时间的推移，这将变得更为普遍。这种风格有很多内涵，最明显的是你能选择一个好地方去生活，这一点是不能被忽视的。但还有另一面：如果你要在某件事情上花费大量时间，这件事情要是你喜欢的，而且还要有这样的机会，那些最会做你所喜欢的事的人就在你身边。在任何地方工作有两种途径，一是为你自己，另外是为了与你一块儿工作的人。为了与最棒的人一块儿工作，就不要管这些人在什么地方：没别的，就因为这些人不会为了你而搬家的。所以，选好你想干的工作，你就能在任何地方与任何人做任何事情了，至少当我们谈论软件开发时就能如此。这也意味着，你要与，在地理上，文化上，经济上，语言上，或诸如此类方面，和你都有巨大差距的同事一块儿工作。要尊重这一现实。把你自己从工作地点的束缚中解放出来，追求在任何地方与任何人一块儿工作的自由吧，它将开启巨大的机遇。<br />
<br />
<strong><span style="font-size: 12pt;">来自硬件的自由</span></strong><br />
&nbsp;&nbsp;&nbsp; 最近有一些关于3D打印的议论，讨论该项技术将怎样把制造过程转换到头脑中。想像一下，当你要制造什么时，你无需操心建立一个工厂来制造这东西，你所需要的只是你的主意和设计能力。是不是很奇妙？肯定是了。但如果你是一名软件开发员，这一想法现在就能成真。如果你有任何想法，你不必购买/订购/安装/构建一个数据中心：你已拥有所需要全部设备，5分钟就能搞定。大量的云服务提供商在幕后做着这些工作，你所要的只是一个好主意和代码。噢，还记得先前我们提到的多平台技术吗？是的，在云计算里它也是有效的：确保你不会上某人的当，所以要明智地选择与它打交道的方法。当然，没有这个问题也还有其它很多问题。测试，实现，发展甚至于抛弃你的想法的自由已经正在改变着软件开发。如果你认为云计算与以前那种在某处使用一台服务器进行同地协作的东西一样的话，那你就应该<a href="http://www.rackspace.com/cloud/">马上去使用它</a>。把你的思想从硬件约束中解放出来，这需要花时间，所以现在就开始吧。<br />
<br />
<strong><span style="font-size: 12pt;">你自己的自由...</span></strong><br />
&nbsp;&nbsp;&nbsp; 如你所见，现在这些可能都成为了现实。但就现实来看，对大多数开发者来说，这些可能性并不能在指日间就成真的。但它可以是你自己的。那么，未来是什么呢？未来使越来越多的开发者们从这些自由中受益，意味着更多的想法能够见到天日，能够学习和创建更多的开源软件，有更多的设备和供应商运行开发出来的程序，更多来自于不同地方的人在一起工作。<br />
&nbsp;&nbsp;&nbsp; 是的，这也意味着更多的挑战：我们需要更多的程序能并行地运行在一个平台上。如前所示，我们会从多核设备中受益。我们的框架需要支持云计算环境。还有，这些新的语言将表现得比今天已有的语言更好，我们也将从中受益。而且，将会有新的框架和抽象能使我们在这样的环境中提高生产率。这些已不是新闻了：这是发展中的世界。记住，软件开发是复杂的，无论应用提供商们怎样试图去摆弄它，软件开发都不可能变得更为简单。<br />
&nbsp;&nbsp;&nbsp; 有大量来自于发展中国家，像BRIC(巴西，俄罗斯，印度，中国)，的开发者，因为有开源软件，他们会学到很多东西；因为有云计算，他们会有更多的机会；因为有远程工作，他们会有更多的工作。软件开发将会更有包容性，因为那些自由性会给予更多开发者以机会让他们能在这方面花费更多的时间，并溶入其中。更多的开发者意味着更多的想法，更多的设备，更多的框架，更多的语言，更多的社区，以及更多的开源软件。软件开发是工程学与艺术的混合体，有更多的艺术家只会是好事儿！<br />
&nbsp;&nbsp;&nbsp; 所以，简言之：为了能在未来生存下去：学着在云环境中部署应用，这会使你独立于特定的供应商和平台。学着与来自于不同文化的人们一块儿工作，加入到开源创新中，这样的话，你会成为更好地开发者，也会变得更受重视。从开源软件中，学习如何基于其他人创建的软件去创建你自己的软件，然后再发布一个简单的解决方案，以便其他人能基于你的软件去创建新的软件。考虑到服务问题，云计算将提供这一功能，并且新出现的设备也能接受它。<a href="http://twitter.com/neal4d">Neal Ford</a>提到"<a href="http://memeagora.blogspot.com/2006/12/polyglot-programming.html">通晓多种语言的程序员</a>"，因为"未来的应用将利用多语言世界的特性。"成为其中的一员吧！<br />
&nbsp;&nbsp;&nbsp; 终于要谈到，如果你是一名Java程序员又当如何呢？那么，你就处于正确的道路上。你理解多平台和标准的重要性。Java是开源软件使用的最重要的语言之一，而且Java最重要的特性就是它本身是开源的，所以你会感轻松。Java也是云计算供应商们的主要关注点，另外像Hadoop这样的Java软件构成了许多云环境的主干。更不要说，所有重要的新潮语言都正谈论着要运行在JavaVM上，还要与已有的Java类库和知识相集成。<br />
<br />
&nbsp;&nbsp;&nbsp; 最后的问题就是你的激情：为未来作准备，选择能让你兴奋的东西，考虑云计算，基于开源软件去创建新的开源软件，加入或吸引来自于不同地方的人们。保持你的独立性。你就将干得很棒！<br />
<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/346151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-03-13 18:22 <a href="http://www.blogjava.net/jiangshachina/archive/2011/03/13/346151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--多线程编程(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 20 Nov 2010 15:49:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/338571.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/338571.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/338571.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">你所不知道的五件事情--多线程编程</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中<a href="http://www.ibm.com/developerworks/java/library/j-5things15/index.html">5 things系列文章中的一篇</a>，讲述了关于多线程的一些应用窍门，值得大家学习。(2010.11.22最后更新)<br />
<br />
摘要：多线程编程不轻松，但它确实能帮助理解JVM如何细微地处理不同代码结构。Steven Haines将分享的5个窍门会帮助你在处理同步方法，volatile变量以及原子类时做出更为合理的决定。<br />
<br />
&nbsp;&nbsp;&nbsp; 尽管很少有Java开发者能够忽略多线程编程，且Java平台类库支持它，甚至于更少的开发者能有时间去深入学习线程。相反，我们只是泛泛地学习线程，如果需要的话，会向我们的工具箱中添加新的技巧和技术。通过这种方法你可能会构建且运行好的应用程序，但你还能做得更好。理解Java编译器和JVM的线程特性，可以帮助你编写更高效，性能更佳的Java代码。<br />
&nbsp;&nbsp;&nbsp; 在<a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=5+things+you+did">5 things系列</a>的本期文章中，我会介绍一些使用同步方法，volatile变量和原子类等多线程编程的细节方面。我的讨论特别关注在这些程序结构是如何与JVM和Java编译器进行交互的，以及不同的交互是如何影响Java应用程序性能的。<br />
<br />
<strong><span style="font-size: 12pt;">1. 同步方法与同步块</span></strong><br />
&nbsp;&nbsp;&nbsp; 你偶尔会衡量是否同步整个方法调用，或者只是同步方法中线程安全的子块。在这种情况下，知道Java编译器在何时将源代码转化为字节码是有帮助的，它在处理同步方法和同步块时是完全不同的。<br />
&nbsp;&nbsp;&nbsp; 当JVM在执行同步方法时，执行线程标识方法的method_info结构设有ACC_SYNCHRONIZED标记，然后它自动地获取对象的锁，调用方法，再释放锁。如果发生了异常，线程会自动释放锁。<br />
&nbsp;&nbsp;&nbsp; 另一方面，同步一个方法块，绕开JVM内建的对获取对象锁和异常处理的支持，这些功能要显式的写在字节码中。如果你读过含有同步块的方法的字节码，你将看到更多的额外操作去管理该功能。清单1展示了生成同步方法与同步块所产生的调用：<br />
<br />
<strong>清单1. 两种同步方法</strong><br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.geekcap;<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SynchronizationExample&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;synchronizedMethodGet()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;synchronizedBlockGet()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">(&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">&nbsp;)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
synchronizedMethodGet()方法生成下列字节码：<br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_0<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;getfield<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;nop<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;iconst_m1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;ireturn</span></div>
<span style="font-size: 10pt;"><br />
而下面是synchronizedBlockGet()方法的字节码：<br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_0<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;dup<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;astore_1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;monitorenter<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_0<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;getfield<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">6</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;nop<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">7</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;iconst_m1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">8</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">9</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;monitorexit<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">10</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;ireturn<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">11</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;astore_2<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_1<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">13</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;monitorexit<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">14</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;aload_2<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">15</span><span style="color: #000000;">:&nbsp;&nbsp;&nbsp;&nbsp;athrow</span></div>
<span style="font-size: 10pt;"><br />
创建同步块会产生16行字节码，然而同步方法只返回5行代码。<br />
<br />
<strong><span style="font-size: 12pt;">2. ThreadLocal变量</span></strong><br />
&nbsp;&nbsp;&nbsp; 如果你想为一个类的所有实例维护单个变量实例，你将使用静态类成员变量来实现这一点。如果你想在每个线程中维护一个变量的实例，你将使用thread- local变量。ThreadLocal变量不同于平常的变量，在于每个线程有它自己的变量初始化实例，通过get()或set()方法可以访问这些变量。<br />
&nbsp;&nbsp;&nbsp; 让我们说，你正在开发多线程代码追踪器的目的是从你的程序去唯一地标识每个线程的路径。挑战在于你需要在跨越多个线程的多个类中协调多个方法。没有 ThreadLocal，这将是一个很复杂的问题。当一个线程开始执行时，它将生成一个唯一的标记以便于在追踪器中进行标识，并在在路径中将这个唯一标记传给每个方法。<br />
&nbsp;&nbsp;&nbsp; 使用ThreadLocal，问题就变得简单了。线程在运行的开始时初始化thread-local变量，然后在各个类的各个方法中去访问它，这就能确保该变量只会在当前执行线程中维护路径信息。当线程执行完毕时，线程会将它的特定路径传递给一个管理对象，该对象负责维护所有的路径。<br />
&nbsp;&nbsp;&nbsp; 当你需要基于每个线程来存储变量时，使用ThreadLocal就很有意义。<br />
<br />
<strong><span style="font-size: 12pt;">3. volatile变量</span></strong><br />
&nbsp;&nbsp;&nbsp; 我估计一大半Java开发员知道Java语言含有关键字volatile。其中大约只有10%的人知道它的意义，只有更少的人知道如何高效地使用它。简言之，将一个变量使用volatile关键字进行标识就意味着该变量的值将被不同的线程修改。为了充分理解volatile关键字的功用，首先就会帮助我们理解线程是如何处理非volatile变量的。<br />
&nbsp;&nbsp;&nbsp; 为了改进性能，Java语言规范允许JRE在各个线程中维护一份针对某个变量的引用的复本。你能够认为这些变量的"thread-local"复本类似于缓存，这会帮助线程避免在每次需要访问该变量的值时都去检查主内存。<br />
&nbsp;&nbsp;&nbsp; 但考虑下面场景可能会发生的事情：两个线程都启动了，第一个线程读到变量A的值为5，而第二个线程读到变量A的值为10。如果变量A已经从5变到10了，然后第一个线程并不会意识到这一变化，所以它会得到A的错误值。如果变量A被标记为volatile，然后在任何时候，某个线程读取A的值时，它都将查询 A的主复本并读到它的当前值。<br />
&nbsp;&nbsp;&nbsp; 如果应用中的变量不会改变，那么使用一个thread-local缓存将是有意义的。另外，知道volatile关键字能为你做些什么也是很有帮助的。<br />
<br />
<strong><span style="font-size: 12pt;">4. volatile对于同步</span></strong><br />
&nbsp;&nbsp;&nbsp; 如果变量被声明为volatile，就意味着它会被多个线程所修改。很自然地，你会希望JRE能为volatile变量以某种方式强制执行同步。幸运地是，当访问volatile变量时，JRE隐式地提供了同步，但会伴随一个很大的代价：读volatile变量是同步的，写volatile变量也是同步的，但非原子性操作不能怎么做。<br />
&nbsp;&nbsp;&nbsp; 这就意味着下面的代码不是线程安全的：<br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">myVolatileVar</span><span style="color: #000000;">++</span><span style="color: #000000;">;</span></div>
<span style="font-size: 10pt;"><br />
前面的语句可以写成如下形式：<br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;temp&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
synchronize(&nbsp;myVolatileVar&nbsp;)&nbsp;{<br />
&nbsp;&nbsp;temp&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;myVolatileVar;<br />
}<br />
<br />
temp</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />
<br />
synchronize(&nbsp;myVolatileVar&nbsp;)&nbsp;{<br />
&nbsp;&nbsp;myVolatileVar&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;temp;<br />
}</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 换言之，如果一个volatile变量按上述方法来进行更新，即先读取值，并修改之，然后再赋值，在两个同步操作之间，这个结果是非线程安全的。你可以考虑是使用同步，还是依赖JRE对volatile变量的自动同步。更好的方法是根据你的用例：如果赋给volatile变量的值依靠于它的当前值(例如加法操作)，如果你想操作是线程安全的，那就必须使用同步。<br />
<br />
<strong><span style="font-size: 12pt;">5. 原子字段更新器</span></strong><br />
&nbsp;&nbsp;&nbsp; 当在多线程环境中加或减一个原始数据类型时，使用java.util.concurrent包中新添加的原子类会比编写你自己的同步代码块要好得多。原子类保证能以线程安全的方式来执行这些操作，如加减数值，更新值，以及添加值。原子类包括 AtomicInteger，AtomicBoolean，AtomicLong，AtomicLong等等。<br />
&nbsp;&nbsp;&nbsp; 使用原子类的挑战在于所有的类方法，包括get，set，以及get-set方法簇都是原子化的。这就意味着read和write操作不会以同步的方式来修改原子变量的值，也不仅仅重要的读-更新-写操作。如果你想对同步代码的发布能有更好的控制，解决方法就是使用原子字段更新器。<br />
<br />
<strong>使用原子更新</strong><br />
&nbsp;&nbsp;&nbsp; 原子字段更新器，如AtomicIntegerFieldUpdater，AtomicLongFieldUpdater和 AtomicReferenceFieldUpdater，是用于volatile字段的基本包装器类。在JDK的内部，Java类库就在使用这些原子类。但在应用程序中，它们还未被广泛使用，你也没有理由不使用它们。<br />
&nbsp;&nbsp;&nbsp; 清单2展示的示例，是一个类使用原子更新来改变某人正在阅读的书：<br />
<br />
<strong>清单2. Book类</strong><br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.geeckap.atomicexample;<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Book<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;name;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Book()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Book(&nbsp;String&nbsp;name&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.name&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;getName()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setName(&nbsp;String&nbsp;name&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.name&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
Book类只是一个POJO(Plain Old Java Object)，只有一个字段：name。<br />
<br />
<strong>清单3. MyObject</strong><br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.geeckap.atomicexample;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.concurrent.atomic.AtomicReferenceFieldUpdater;<br />
<br />
</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;*<br />
&nbsp;*&nbsp;</span><span style="color: #808080;">@author</span><span style="color: #008000;">&nbsp;shaines<br />
&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;MyObject<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">volatile</span><span style="color: #000000;">&nbsp;Book&nbsp;whatImReading;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;AtomicReferenceFieldUpdater</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">MyObject,Book</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;updater&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AtomicReferenceFieldUpdater.newUpdater(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyObject.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;Book.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">whatImReading</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Book&nbsp;getWhatImReading()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;whatImReading;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setWhatImReading(&nbsp;Book&nbsp;whatImReading&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">this.whatImReading&nbsp;=&nbsp;whatImReading;</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updater.compareAndSet(&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.whatImReading,&nbsp;whatImReading&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
&nbsp;&nbsp;&nbsp; 清单3中的MyObject类揭露了whatImReading属性就是你所期望的，该属性有get和set方法，但set方法做的一些事情不太一样。不同于简单地将内部的Book引用赋予一个特定的Book对象(使用清单3中被注释的代码就可以做到这一点)，该示例使用了一个 AtomicReferenceFieldUpdater。<br />
<br />
<strong>AtomicReferenceFieldUpdater</strong><br />
Javadoc对AtomicReferenceFieldUpdater有如下定义：<br />
&nbsp;&nbsp;&nbsp; 一个基于反射的工具类，它能对指定类的指定的volatile引用字段进行原子更新。该类被设计用于原子数据结构，在这种结构中，相同节点的多个引用字段会进行独立地原子更新。<br />
&nbsp;&nbsp;&nbsp; 在清单3中，通过调用AtomicReferenceFieldUpdater的静态方法newUpdater就能创建它的实例，该方法要接收三个参数：<br />
&nbsp;&nbsp;&nbsp; 包含该字段的对象的类(在这个例子中，就是MyObject)<br />
&nbsp;&nbsp;&nbsp; 将被自动更新的对象的类<br />
&nbsp;&nbsp;&nbsp; 将被自动更新的字段的名称<br />
<br />
在执行getWhatImReading方法获取实际值时没有使用任何形式的同步，然而setWhatImReading方法的执行则是一个原子操作。<br />
&nbsp;&nbsp;&nbsp; 清单4证明了如何去使用setWhatImReading()方法，以及如何判断变量的值进行了正确地修改：<br />
<br />
<strong>清单4. 练习原子更新的测试用例</strong><br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.geeckap.atomicexample;<br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.junit.Assert;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.junit.Before;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.junit.Test;<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;AtomicExampleTest<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;MyObject&nbsp;obj;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Before<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setUp()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MyObject();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setWhatImReading(&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Book(&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Java&nbsp;2&nbsp;From&nbsp;Scratch</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;)&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testUpdate()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setWhatImReading(&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Book(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Pro&nbsp;Java&nbsp;EE&nbsp;5&nbsp;Performance&nbsp;Management&nbsp;and&nbsp;Optimization</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;)&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Incorrect&nbsp;book&nbsp;name</span><span style="color: #000000;">"</span><span style="color: #000000;">,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Pro&nbsp;Java&nbsp;EE&nbsp;5&nbsp;Performance&nbsp;Management&nbsp;and&nbsp;Optimization</span><span style="color: #000000;">"</span><span style="color: #000000;">,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.getWhatImReading().getName()&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}</span></div>
<span style="font-size: 10pt;"><br />
查看资源以学习更多关于原子类的知识。<br />
<br />
<strong><span style="font-size: 12pt;">结论</span></strong><br />
&nbsp;&nbsp;&nbsp; 多线程编程总是存在着挑战性，但涉及到Java平台，它已经获得了支持去简化一些多线程编程任务。在本文中，我讨论了你在基于Java平台编写多线程应用时可能不知道的五件事情，包括同步方法与同步块的不同之处，使用ThreadLocal变量为每个线程去存储值，针对volatile关键字的广泛误解 (包括在需要同步时依赖volatile所产生的危险)，还简要地看了一下原子类的复杂之处。查看资源以学习到更多相关知识。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/338571.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-11-20 23:49 <a href="http://www.blogjava.net/jiangshachina/archive/2010/11/20/338571.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--改进Swing(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 25 Oct 2010 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/336125.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/336125.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/336125.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 你所不知道的五件事情--改进Swing这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇，讲述了关于改进Swing应用的一些窍门，值得大家学习。(2010.10.25最后更新)摘要：Swing已是一个比较老的工具集了，在美观的用户界面出来之前需要开发很长时间。它缺少一些你在开发富UI时所需的组件。幸运地是，像 Substance，Swi...&nbsp;&nbsp;<a href='http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html'>阅读全文</a><img src ="http://www.blogjava.net/jiangshachina/aggbug/336125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-10-25 22:23 <a href="http://www.blogjava.net/jiangshachina/archive/2010/10/25/336125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Android 101--安装(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/10/11/334478.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 11 Oct 2010 13:43:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/10/11/334478.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/334478.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/10/11/334478.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/334478.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/334478.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">Android 101 -- 安装</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是<a href="http://java.dzone.com/">JavaLobby</a>中Android 101系列博文中的<a href="http://java.dzone.com/articles/android-101-system-setup">第二篇</a>，介绍了如何安装Android开发环境。对于我这个Android门外汉来说，至少学习到了一点儿新知识，希望大家也能如此。(2010.10.11最后更新)<br />
<br />
在本系列的上一篇文章，我们看过了Android框架的基本概念。在这篇文章中，我们看看如何在系统中进行Android安装。我假设你已经安装Java超过5年了。为了更方便些，你应该安装Eclipse作为你的开发IDE。<br />
<strong><span style="font-size: 12pt;">设置Android SDK</span></strong><br />
从<a href="http://d.android.com/sdk/index.html">http://d.android.com/sdk/index.html</a>中下载Android SDK。<br />
下载后，将zip文件解压到硬盘的某个路径上。你可以将这个SDK路径加到系统PATH变量中，但这是可选的。<br />
运行SDK安装程序。在Windows中就是<strong>SDK Manager.exe</strong>，而在其它平台中你需要运行<strong>tools/android</strong>。该程序允许你安装SDK组件，例如文档，平台，插件式类库和USB驱动。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_01.PNG" alt="" border="0" /><br />
<br />
一般地，你会想在这儿选择任何东西，以便你能为任何Android目标平台编写应用。<br />
<br />
<strong><span style="font-size: 12pt;">开发工具</span></strong><br />
安装的下一步是在你的Eclipse实例中安装Android开发工具包。最好的方法是使用Help/Install New Software菜单中的升级管理器。只需添加一个指向<a href="https://dl-ssl.google.com/android/eclipse">https://dl-ssl.google.com/android/eclipse</a>的新的升级站点即可。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_02.PNG" alt="" border="0" /><br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_03.PNG" alt="" border="0" /><br />
一旦完成了安装，你还需要重启Eclipse。<br />
<br />
<strong><span style="font-size: 12pt;">第一个Android工程</span></strong><br />
现在你已经准备好进行Android开发了。<br />
首先你要让Eclipse知道到哪里去找Android SDK。打开首选项对话框，点击Android项，设置Android SDK的安装路径：<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_04.PNG" alt="" border="0" /><br />
<br />
最后一步就是确保你有一个运行Android应用的AVD(Android虚拟设备)。你可以使用AVD管理器来做到这一点：<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_05.PNG" alt="" border="0" /><br />
<br />
当使用New Project向导时，你就有机会去创建Android工程了：<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_06.PNG" alt="" border="0" /><br />
<br />
从这儿你能创建一个针对特定Android SDK的工程：<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_07.PNG" alt="" border="0" /><br />
<br />
如果你点击下一步，你还可以创建一个Android测试工程以测试你的应用程序。<br />
在工程中，你还可以看到新创建的活动：<br />
</span>
<div style="background-color: rgb(238, 238, 238); font-size: 13px; border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 98%;"><span style="color: rgb(0, 0, 255);">package</span><span style="color: rgb(0, 0, 0);">&nbsp;com.dzone.android.app;<br />
<br />
</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;android.app.Activity;<br />
</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;android.os.Bundle;<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;FirstActivity&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;Activity&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">/**</span><span style="color: rgb(0, 128, 0);">&nbsp;Called&nbsp;when&nbsp;the&nbsp;activity&nbsp;is&nbsp;first&nbsp;created.&nbsp;</span><span style="color: rgb(0, 128, 0);">*/</span><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;onCreate(Bundle&nbsp;savedInstanceState)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">super</span><span style="color: rgb(0, 0, 0);">.onCreate(savedInstanceState);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.main);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<span style="font-size: 10pt;">
我们将稍作修改，创建自己的所使用的视图：<br />
</span>
<div style="background-color: rgb(238, 238, 238); font-size: 13px; border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 98%;"><span style="color: rgb(0, 0, 255);">package</span><span style="color: rgb(0, 0, 0);">&nbsp;com.dzone.android.app;<br />
<br />
</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;android.app.Activity;<br />
</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;android.os.Bundle;<br />
</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;android.widget.TextView;<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;FirstActivity&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;Activity&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">/**</span><span style="color: rgb(0, 128, 0);">&nbsp;Called&nbsp;when&nbsp;the&nbsp;activity&nbsp;is&nbsp;first&nbsp;created.&nbsp;</span><span style="color: rgb(0, 128, 0);">*/</span><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<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);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;onCreate(Bundle&nbsp;savedInstanceState)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">super</span><span style="color: rgb(0, 0, 0);">.onCreate(savedInstanceState);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TextView&nbsp;view&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;TextView(</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;view.setText(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">A&nbsp;New&nbsp;App</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;setContentView(view);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;"><br />
为了看看这是否能工作，点击Run As/Android application。这会启动Android模拟器，并运行你新创建的应用程序。<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/android101/andrsetup_08.PNG" alt="" border="0" /><br />
<br />
既然，你已经在系统安装好了Android，本系列的后继文件将会带你看看编写你自己的Android应用的过程。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/334478.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-10-11 21:43 <a href="http://www.blogjava.net/jiangshachina/archive/2010/10/11/334478.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Android 101--基础(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/10/07/333917.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Thu, 07 Oct 2010 13:25:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/10/07/333917.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/333917.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/10/07/333917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/333917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/333917.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">Android 101 -- 基础</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是<a href="http://java.dzone.com">JavaLobby</a>中Android 101系列博文中的<a href="http://java.dzone.com/articles/android-101-basics">第一篇</a>，介绍了Android开发的基本概念。对于我这个Android门外汉来说，至少学习到了一点儿新知识，希望大家也能如此。(2010.10.07最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 在当前的软件开发世界中，编写移动应用已变得非常流行了。对开发框架的选择还不太多，可用的智能手机的数量也有限。尽管编写运行在iOS上的应用十分流行，但Android已经获得了Java开发者们的关注。在之后的若干篇文章中，我将采取一些步骤帮助你入门Android开发。但在我们开始编码之前，让我们先看看Android开发的一些核心原理。<br />
<br />
<strong><span style="font-size: 12pt;">什么是Android？</span></strong><br />
下面这张来自于在线的开发者手册的图片展示了Android操作系统的架构：<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/system-architecture.jpg" alt="" border="0" /><br />
Android构建在<strong>Linux内核</strong>之上，Linux内核会管理典型的操作系统服务。<strong>Android运行时环境</strong>基于Dalvik虚拟机(目前Oracle 正给予许多关注的东西)。Dalvik是Java虚拟机的一个瘦身后的变种，它基于Apache Harmony的Java实现。不提供JavaME，Swing或AWT组件；相反地，Android提供它自己的组件库。<br />
&nbsp;&nbsp;&nbsp; 还有一些由C/C++编写的<strong>本地类库</strong>。Surface Manager处理针对展现子系统的访问，而媒体类库提供了针对最流行音频和视频格式的播放功能。<br />
&nbsp;&nbsp;&nbsp;
<strong>应用框架</strong>是大部分开发者所感兴趣的，它提供了创建合格Android移动应用所需的API和服务。<br />
<br />
<strong><span style="font-size: 12pt;">Android核心概念</span></strong><br />
如前所述，应用框架拥有开发者创建应用程序所需的一切。下面是主要概念的纲要：<br />
<strong>活动</strong><br />
一个活动是一个用户界面屏幕。一个应用程序在应用的执行过程中可以有一个或多个活动。你所创建的每一个活动可以拥有一个它自己的窗口以进行图形绘制。<br />
<strong>服务</strong><br />
服务作为后台任务运行，它没有可视化的表现形式。与活动一样，服务运行在主应用进程线程中，但服务常会派生出其它的线程来执行任务，而不会影响其它应用的运行。服务的典型例子就是音乐播放器，当它在播放列表中的歌时，你的手机还可以做其它事情。<br />
<strong>内容提供器</strong><br />
内容提供器是一个客户化API，它允许读写特定的数据集。它就允许不同的应用彼此之间共享数据。内容提供器都是ContentProvider基类的子类，ContentProvider提供一个访问数据的标准接口。应用程序不能直接调用ContentProvider的实现，转而要使用 ContentResolver对象，该对象可以访问任何ContentProvider实现。<br />
<strong>Intent</strong><br />
Intent是一个特定的活动，例如发送电子信件，播放歌曲，或发起一次联系。<br />
<strong>资源</strong><br />
Android应用拼图中的最后一块就是资源--应用程序需要访问或展示的图片，文本或非编码的信息。<br />
<br />
<strong><span style="font-size: 12pt;">活动生命周期</span></strong><br />
下图展示了一个活动在其生命周期中的不同状态：<br />
<img src="http://www.blogjava.net/images/blogjava_net/jiangshachina/activity_lifecycle.png" alt="" border="0" /><br />
当你不能控制状态间的转换时，你可以通过onX()方法，像onStart()，onResume()等等，来获知状态的改变。你可以重写这些方法以使应用程序能恰当地对这些状态作出反应。<br />
<br />
<strong><span style="font-size: 12pt;">下一步</span></strong><br />
现在你已对Android应用是如何组成的有了更好的理解，这为开始真正的开发作好了准备。下一篇文章将带你看看系统设置。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/333917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-10-07 21:25 <a href="http://www.blogjava.net/jiangshachina/archive/2010/10/07/333917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tony Printezis和Raghavan Srinivas插话Java的未来(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/09/22/332661.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 22 Sep 2010 14:24:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/09/22/332661.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/332661.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/09/22/332661.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/332661.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/332661.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">Tony Printezis和Raghavan Srinivas插话Java的未来</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是Oracle<a href="http://blogs.oracle.com/javaone/">官方JavaOne博客</a>中的<a href="http://blogs.oracle.com/javaone/2010/09/rock_stars_tony_printezis_and_raghavan_srinivas_chime_in_on_the_future_of_java.html">一篇</a>，介绍了两位Java Rock Start--Tony Printezis和Raghavan Srinivas对他们所感兴Java主题的看法。(2010.09.22)<br />
<br />
我逮到了两个JavaOne Rock Start，Oracle的Tony Printezis，以及以不断探索新知识而闻名的Java技术布道者Raghavan Srinivas，让他们谈谈关于Java和JavaOne的事儿。<br />
<br />
我向Printezis，垃圾收集和Java方面的领导性专家，询问今年的JavaOne如何。<br />
"对于JavaOne而言这是重要的一年，它有了一个新的九月时间点，三个新会场，一个新主人。但尽管有了这些变化，它仍然保持了早前的宗旨，即让人们喜欢从该领域的顶级专家那儿学习Java平台的最新开发技术。所以，尽管有了这些改变，我坚信与会者们会发现会议内容仍如往年那样富有教育性的，有用的，且是高质量的。另外，JavaOne与OpenWorld共用同一会场也将给予会者们机会去利用这两个活动，这会使他们在San Francisco的旅程更有价值。"<br />
<br />
那么Java平台的现状如何？<br />
"很明显地，2010年是有重大变故的一年，这不可避免地会影响到我们每天的工作与进步。然而，Oracle已经承诺会继续发展和投资Java平台。所以，尽管有过去数月中我们所遇到的混乱情况，我们仍会像以前那般创劲十足地继续创新和改进Java平台。"<br />
<br />
Java开发者应该关心的趋势是什么？<br />
"对于使其它语言能运行JVM上的工作我都感兴趣。这些语言都能方便地与Java进行交互(毕竟都是Java字节码！)，这一事实为开发者提供了许多有趣的机会。使用最适合的语言去编写系统的每一个部分，并且所有部分都运行在同一个最高水准的JVM上，也使得在跨语言边界时能够做到最好，这样不是很好吗？"<br />
<br />
我问到他在周四的议程，"垃圾收集终结者"，这是一个很受欢迎的主题，会占用整个上午。在下午，他还会提供另一个议程。<br />
"多年来，我一直在向顾客和开发者们介绍GC。我当时很快就有这样的印象，很多伙计们对GC能做什么和不能做什么存在着误解。事实上，我们已收到许多建议GC进行改进的要求，但这些要求毫无道理。所以，我和我的合作演讲者John Coomes就想给出一个演讲来帮助听众们去澄清如此多的误解。"<br />
<br />
Tony也给出了一个关于在HotSport Java虚拟机中进行性能调优的议题<br />
"这是我和Charlie Hunt在去年JavaOne中相同主题的后续演讲。我们在开始这个演讲时就声明'GC调优是一种艺术，我们不能给出一种通用的秘方来教你怎么去做。'今年我们还将这么做。我们提出了一个方法学，应该可以适应很多不同的情况。我们也收到了来自于Jon Masamitsu，GC组的技术领导，的许多有用信息。这需要大量的工作，所以我希望能向大家展示它。希望听众们会发现这个演讲会很有帮助，也希望这个演讲像去年那样受欢迎。"<br />
<br />
Q：你想看到GC在未来如何发展？<br />
A：更大的内存，更佳的延迟情况，更多的垃圾！<br />
<br />
<strong>关于垃圾收集的八个神话</strong><br />
出于好奇心，此处有一个GC神话的列表，Tony将在周四曝出来：<br />
<br />
&nbsp;&nbsp; 1. 引用计数GC将解决我所有的延迟问题。<br />
&nbsp;&nbsp; 2. Malloc/free将总是比GC表现的更好些。<br />
&nbsp;&nbsp; 3. 一旦对象变得不可及，就应该立即调用finallizer方法。<br />
&nbsp;&nbsp; 4. 垃圾收集将消除所有的内存泄露。<br />
&nbsp;&nbsp; 5. 由于我知道某些重要对象已不及了，如果我显式地消除分配给这些对象的内在，生活将会变得更好些。<br />
&nbsp;&nbsp; 6. GC既可以给我非常高的吞吐量，也能给我非常低的潜伏期。<br />
&nbsp;&nbsp; 7. 在应用程序的关键部分我需要使GC停止工作。<br />
&nbsp;&nbsp; 8. 在系统中，我能使用GC所编写的任何东西，我也可以使用malloc/free去编写。<br />
<br />
Raghavan Srinivas，Sun Microsystems公司中多年的Java技术布道者，在关于技术的发展方向方面有些东西要说。<br />
我让他告诉我们下一个大的技术革新会是什么。<br />
"Hadoop和NoSQL运动。Hadoop是一个Apache开源项目，它基于代码的转移及数据的本地化，使你不必处理数据延迟。当要处理当今企业生产和消费的TB甚至PB级数据时，这会很有帮助。它为了开发者们提供了一个非常简单的基于Map/Reduce的范式，并且这个框架隐藏了分布式数据加载，数据复制等等方面的大部分底层复杂度。如果你熟悉Peter Deutsh的计算谬论，你就能发现这些谬论已经困扰着许多分布式系统了。新的框架解决了许多这样的问题。对于云计算，这是一个极好的基础架构。公司，无论大小，已经采用了这些技术，而最初的热情让我记住了Java。"<br />
<br />
<strong><span style="color: red;"><span style="font-size: 12pt;">祝大家中秋佳节愉快:-)</span></span></strong><br />
</span><img src ="http://www.blogjava.net/jiangshachina/aggbug/332661.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-09-22 22:24 <a href="http://www.blogjava.net/jiangshachina/archive/2010/09/22/332661.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你所不知道的五件事情--JVM的命令行选项(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Wed, 01 Sep 2010 00:53:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/330341.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/330341.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/330341.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong><span style="font-size: 14pt;">你所不知道的五件事情--JVM的命令行选项</span></strong></span><br />
</div>
<span style="font-size: 10pt;">这是Ted Neward在<a href="http://www.ibm.com/developerworks/">IBM developerWorks</a>中<a href="http://www.ibm.com/developerworks/java/library/j-5things11/index.html">5 things系列文章中的一篇</a>，讲述了关于JVM命令行参数的一些应用窍门，值得大家学习。(2010.09.01最后更新)<br />
<br />
摘要：Java虚拟机有数百个命令行选项，只有经验十分丰富的Java开发员才会使用这些选项去调优Java运行时环境。学习如何监控并记录编译器性能，禁用显示的垃圾收集(System.gc())，扩展JRE，及其它。<br />
<br />
&nbsp;&nbsp;&nbsp; JVM是Java应用程序功能与性能背后的实际工作者，大部分Java开发者认为这是理所当然的。然而我们中很少有人真正地理解JVM是如何做到这些事的--如，分配并收集垃圾对象，摆弄线程，打开及关闭文件，解释和/或使用JIT编译Java字节码，以及其它任务。<br />
&nbsp;&nbsp;&nbsp; 不熟悉JVM不仅会使你在应用程序的性能方面付出代价，而且当JVM出了某些问题时，还很难进行修复。<br />
&nbsp;&nbsp;&nbsp;
<a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=5+things+you+did">5 things系列</a>的这篇文章介绍一些好用的命令行JVM选项，你可以使用它们去诊断并调优Java虚拟机的性能。<br />
<br />
<strong><span style="font-size: 12pt;">
1. DisableExplicitGC</span></strong><br />
&nbsp;&nbsp;&nbsp; 我无法告诉你我已经多少次被要求就应用程序性能问题进行咨询了，而我只是简单地对代码文件执行grep命令就能发现如清单1所示的程序--这就是早期Java性能的反模式：<br />
<br />
<strong>Listing 1. System.gc();</strong><br />
</span>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;We&nbsp;just&nbsp;released&nbsp;a&nbsp;bunch&nbsp;of&nbsp;objects,&nbsp;so&nbsp;tell&nbsp;the&nbsp;stupid<br />
</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;garbage&nbsp;collector&nbsp;to&nbsp;collect&nbsp;them&nbsp;already!</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">System.gc();</span></div>
<span style="font-size: 10pt;"><br />
显式地执行垃圾收集器<em>确实是个坏主意</em>--这就像把你自己与一个疯狂的斗牛犬锁在电话亭中那样。虽然该调用的确切语义依赖于具体的实现，但假设你的JVM运行着一个分代垃圾收集器(大多数JVM也正是如此)，System.gc()方法强制要求VM对Heap进行"全面清扫"，即便有些并不是必要的。一般地，全面清扫的开销要比常规GC操作要大几个数量级，而常规GC操作的开销只会产生纯数学的负作用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;
但不要相信我的话--为了这个特殊的人为错误，Sun的工程师们为我们提供了一个JVM选项：<span style="font-family: Courier;">-XX:+DisableExplicitGC</span>选项自动地将<span style="font-family: Courier;">System.gc</span>()调用置为无用操作，给你一个机会去执行程序，并让你自己看看<span style="font-family: Courier;">System.gc</span>()是否已经帮助或损害整个JVM的执行。<br />
<br />
<strong><span style="font-size: 12pt;">2. HeapDumpOnOutOfMemoryError</span></strong><br />
&nbsp;&nbsp;&nbsp; 你肯定曾经遇到过JVM不断死掉的情况，即抛出<span style="font-family: Courier;">OutOfMemoryError</span>，在你的一生中似乎都不能设置一个调试器去捕获这个错误，并看看是出了什么问题？像这些不时发生且/或无法确定的问题能使一个开发员完全疯掉。<br />
&nbsp;&nbsp;&nbsp; 你所想的，就是在在JVM快死掉时能够捕获Heap的快照--这正是命令<span style="font-family: Courier;">-XX:+HeapDumpOnOutOfMemoryError</span>所要做的。<br />
&nbsp;&nbsp;&nbsp; 运行该命令就是告诉JVM创建一个"Heap转储快照"，并将它保存为一个文件以便于处理，常常使用jhat工具(在<a href="http://www.ibm.com/developerworks/java/library/j-5things11/index.html#resources">前一篇文章</a>中我有介绍过)。使用相应的命令<span style="font-family: Courier;">-XX:HeapDumpPath</span>，就指定被保存文件的确切路径。(不论文件保存在何处，要先确保文件系统和/或Java进程有必要的仅限配置能在那儿写文件。) <br />
<br />
<strong><span style="font-size: 12pt;">3. bootclasspath</span></strong><br />
&nbsp;&nbsp;&nbsp; 偶尔地，将某个类，该类不同于原有的或以某种方式扩展而得到的JRE所存储的类，置于类路径中是有用的。(一个例子就是新的Java Crypto API适配器)。如果你想扩展JRE，那么你的定制实现需要能用于引导ClassLoader，该ClassLoader会加载<span style="font-family: Courier;">java.lang.Object</span>以及它在<span style="font-family: Courier;">rt.jar</span>中的所有同族成员。<br />
&nbsp;&nbsp;&nbsp; 尽管你可以打开<span style="font-family: Courier;">rt.jar</span>并将你定制实现或新的包放入其中，但在技术上这就违反了当初你在下载JDK时所同意的协议。<br />
&nbsp;&nbsp;&nbsp; 相反地，应该使用JVM自己的<span style="font-family: Courier;">-Xbootclasspath</span>选项，以及它的兄弟选项<span style="font-family: Courier;">-Xbootclasspath/p</span>和<span style="font-family: Courier;">-Xbootclasspath/a</span>。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">-Xbootclasspath</span>允许你设置整个引导类路径，该路径一般地必须包含一个针对<span style="font-family: Courier;">rt.jar</span>的引用，再加上一批随JDK发布但不是<span style="font-family: Courier;">rt.jar</span>一部分的其它JAR文件。<span style="font-family: Courier;">-Xbootclasspath/p</span>会向已有引导类路径中预置值，然后-Xbootclasspath/a会将它连接起来。<br />
&nbsp;&nbsp;&nbsp; 例如，如果你已经修改了原有的<span style="font-family: Courier;">java.lang.Integer</span>，并将修改后的程序放入子目录，<span style="font-family: Courier;">mods</span>，然后参数<span style="font-family: Courier;">-Xbootclasspath/a mods</span>将会在加载原有默认<span style="font-family: Courier;">Integer</span>类之前加载这个新的<span style="font-family: Courier;">Integer</span>实现。<br />
<br />
<strong><span style="font-size: 12pt;">4. verbose</span></strong><br />
&nbsp;&nbsp;&nbsp; 对于任何类型的Java应用，<span style="font-family: Courier;">-verbose</span>都是头等有用的诊断工具。该选项有三个子选项：<span style="font-family: Courier;">gc</span>，<span style="font-family: Courier;">class</span>和<span style="font-family: Courier;">jni</span>。<br />
&nbsp;&nbsp;&nbsp; 一般地，如果JVM垃圾收集器在运行并导致了低下的性能，那么gc就是开发员们第一次想要弄清楚的地方。不幸地是，解释gc的输出需要些技巧，这足够成为一整本书的主题了。更糟地是，在命令行窗口打印出的输出对于不同的Java发行版是不同的，或者对于不同的JVM也是不同的，这造成难以对其进行正确地解释。<br />
&nbsp;&nbsp;&nbsp; 一般来说，如果垃圾收集器是分代收集器(大多数"企业级"VM正是如此)，会有一些布尔型选项去指定是否使用完全清除；在Sun的JVM中，这样的选项会以<span style="font-family: Courier;">[Full GC ...]</span>的形式出现在GC输出行的开头。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">class</span>会是一个生命保护者，它尝试着诊断<span style="font-family: Courier;">ClassLoader</span>和/或不匹配的类冲突。它不仅会报告类是在何时被加载的，而且也会报告类是从何处被加载的，包括JAR文件的路径，如果这个类来自于一个JAR的话。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">jni</span>很少用到，除非你有使用JNI和原生库。当启用该选项时，它会报告各种JNI事件，例如原生库何时被加载的，以及方法何时被绑定的；另外，该报告输出会由于不同的Java版或JVM实现而不尽相同。<br />
<br />
<strong><span style="font-size: 12pt;">5. 命令行-X</span></strong><br />
&nbsp;&nbsp;&nbsp; 我已经列出了一些JVM提供的且我比较喜欢的命令行选项，但还有更多的选项你自己就能发现到。运行命令行参数-X列出所有JVM支持的非标准的(但多数就是安全的)参数--就像这些：<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">-Xint</span>，使JVM以解释模式进行运行(对于测试JIT编译器是否真的作用到你的程序，或是证明你是否有Bug在JIT编译器中，该选项是很用的)。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: Courier;">-Xloggc:</span>，该参数所做的与<span style="font-family: Courier;">-verbose:gc</span>相同，但它会把日志记录到一个文件中，而不是一股脑地输出在命令窗口中。<br />
&nbsp;&nbsp;&nbsp; JVM命令行选项改了一次又一次，所以隔一段时间再去看会是一个好主意。这种区别就好像是，花上一整夜金城汤池盯着你的显示器，或者是下午五点就回家与爱人和孩子享用美餐(或是在Mass Effect 2中屠杀敌人，这取决于你的偏好)。<br />
<br />
<strong><span style="font-size: 12pt;">结论</span></strong><br />
&nbsp;&nbsp;&nbsp; 命令行选项的本意不是为了在产品环境中永久使用--事实上，除了你(可能)最终用于调优JVM垃圾收集器的选项之外，非标准的命令选项都不会被刻意地用到产品中。但作为能够窥探到完全不透明的虚拟机的内部工部情形的工具，它们是无价的。<br />
&nbsp;&nbsp;&nbsp; <a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=5+things+you+didn%27t+know+about">5 things系列</a>的下一篇文章：Java日常工具。<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/330341.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-09-01 08:53 <a href="http://www.blogjava.net/jiangshachina/archive/2010/09/01/330341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Android = Java(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2010/08/22/329582.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sun, 22 Aug 2010 11:49:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2010/08/22/329582.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/329582.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2010/08/22/329582.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/329582.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/329582.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">Android = Java</span></span></strong><br />
</div>
<span style="font-size: 10pt;">近期Java社区正热议Oracle对Google的侵权诉讼，恰巧前几日在<a href="http://www.java.net/">java.net</a>中看到一篇<a href="http://weblogs.java.net/blog/opinali/archive/2010/08/17/android-java">博文</a>，文章作者也借此事件表达了Android=Java的观点。由于我对Java移动开发、Android都没太关注过，文章细细读来，有些许进益，译在此处，可能对大家也有帮助。(2010.08.22最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; Java社区正忙于讨论Orace针对Google Android平台的专利诉讼。我已在多个场合道出了我的观点，但还是需要一个评论性的文章来重复一下我在每个场合中都作出的相同评论...所以，本博文会将我的想法竹筒倒豆子般地和盘托出。<br />
<br />
<strong><span style="font-size: 12pt;">第八个千禧年问题：Android=Java？</span></strong><br />
&nbsp;&nbsp;&nbsp; 几日前，有声明称有研究者已证明了P!=NP，这导致了编程社区中的极大热忱--至少是直到两天前，当第一个评阅者指出了该证据中几处错误时。在我的计算机科学专业中我已学过这门课，但不能否认地是，我不懂高等数据以至于不能理解这些证明(P=NP?是克雷数学研究所的千禧年问题之一就是一个很好的理由)。所以，让我们谈谈一个更为简单的等式：<strong>Android是否等价于Java？</strong>注意，我说的不是<em><strong>等于</strong></em>，而是<em><strong>等价</strong></em>，就如同P=NP那样。<br />
<br />
<strong><span style="font-size: 12pt;">等价的类/字节码格式</span></strong><br />
&nbsp;&nbsp;&nbsp; 在许多层面上，Android = Java是明显正确的。Android应用使用Java语言编写，使用JDK中的javac编译器(或其等价品，如ECJ)进行编译。这就产生了标准的 Java字节码(<em>.class</em>文件)。然后，这些文件被转化成Android的.dex文件，实际上就是Java类的另一种不同格式的文件罢了。但你也能将GIF图像转化成优质的PNG格式，尽管这两种图像文件的字节码流完全不同，但它们都同样的优秀。<br />
&nbsp;&nbsp;&nbsp; 等价的文件格式有大量的实现细节，常常是为了优化。例如，如果我们不需要复杂且不同的跨框架压缩技术，仅仅是满足于低效率的视频流，我们就能避免所有违反MPEGLA视频编码专利的麻烦了。<br />
&nbsp;&nbsp;&nbsp; Android设计不同的类文件出于多种动机；但绕开Sun的知识产权肯定是主因。不管怎样，Google不能远离Java。这两种类文件格式非常等价。它们只是在低层次的数据结构方面不同，但它们的语法是一致的，也存储完全相同的信息。我可以肯定JavaSE或JavaME的VM能很容易地在它的系统类加载器中加入一个.dex解析器去加载"Android类"。<br />
&nbsp;&nbsp;&nbsp; Android SDK<strong>依赖</strong>于<em>.java -&gt; .class -&gt; .dex</em>转换，这一转换精于细节且是无损的。"无损"是非常重要：虽然GIF=PNG，但有损的JPG文件则不能完全相等--它不能解码出完全相同的信息。如果 JVM和Dalvik真是完全独立的，你就很难写出一个相对简单的工具无妥协的去将编译后的代码从一种形式转换到另一种形式：没有信息丢失；当为了补偿在一个VM所拥有而在另一个VM却没有的重要特性时；转换后的文件不会变大；当按照一个VM的核心API去实现另一个VM的核心API时，不需要额外的运行时层。<br />
&nbsp;&nbsp;&nbsp; (<em>我知道dx翻译器有多么的复杂。我已看过它的源代码。字节码翻译器很大，它是一个完整的反编译/重编译器，完全使用SSA构建。但这种翻译只是在理论上比较细微；但在设计上，从Java到Dalvik字节码的影射还是平滑的。栈相对于寄存器架构只是在优化细节方面有所不同；而在关键方面，如VM层的类型系统，则是一致的。</em>) <br />
<br />
<strong><span style="font-size: 12pt;">等价的虚拟机</span></strong><br />
&nbsp;&nbsp;&nbsp; 也能很容易的证明Dalvik=JVM。不仅仅是在源代码或字节码格式方面：它们的运行时对应部分也是如此。一旦Dalvik VM加载了一个"Android类"，<strong>它走起来像Java类，叫起来也像Java类</strong>。如果你懂Java编程(能了解其高级和底层细节)，你就懂 Android编程。只是一个学习新API和框架理论的问题罢了。它们是等价的系统。<br />
&nbsp;&nbsp;&nbsp; 还记得Microsoft的.NET吗？当.NET被介绍出来时，Java社区很快就公开抨击.NET只是Java的翻版。当时我也在那群抨击的人群中，但现在我知道这么做会更好些。然而.NET是一个庞大的翻版；C# 1.0语言。区别任一语言程序的最方便方法就是风格规范--如，<em>toString()</em>相对于<em>ToString()</em>。但在更关键的VM规范方面，Microsoft做了很好的功课。CLR，CLI和核心框架都完全不同于Java，所以我们不能说JVM=CLR。你不能使用一个简单的文件格式转换器工具作用于被编译的Java类，然后将获得信息直接运行在纯粹的.NET运行时环境上。<br />
&nbsp;&nbsp;&nbsp; 想要证据吗？只要看看<a href="http://www.java.net/">IKVM</a>。这是一个非常有趣的项目，它能使Java跨越编译运行在.NET环境中，所以你的Java代码可不经过修改运行在CLR之上(或其它同等的.NET运行时环境，如Mono)...但<a>IKVM</a>并<strong>不是</strong>一个简单的类dx文件转换器。对于任何超过HelloWorld的应用程序，将 Java类及其核心API适配转换到.NET是非常复杂的。每个平台的内在特性，像反射，安全，并发，异常处理，字节码规范，I/O及其它核心API，在特性上都非常相似，但在细枝末节处则完全不同--强行让IKVM......，则Java代码能运行在.NET VM上。这也需要一个非常庞大的额外的运行时层，基本上要把全部的OpenJDK源代码适配到JavaSE API。我追踪IKVM的开发已有多年了--通过阅读极棒的<a href="http://weblog.ikvm.net/">IKVM Blog</a>--所以我有一个需要作出大量努力的好主意去将Java代码和JavaSE应用适配到.NET。(该工作还没全完成；已完成的部分则常有些性能问题。) <br />
&nbsp;&nbsp;&nbsp; (<em>旧有的<strike>Visual J++</strike> Visula J#并不是一个简单的Java-.NET解释器。我不会讨论它，但完全可以说Visual J#对Java的兼容要大大劣于哪怕是非常早期的IKVM版本。</em>) <br />
&nbsp;&nbsp;&nbsp; 在讨论中我引入了P=NP的例子；有些人可能会引入图灵等价，并说任何图灵完备的平台/语言/VM都是相互等价的。确实如此，但和我们要讨论的问题并不相干。图灵模型过于通用；如果仅按表面意思来使用它将会摧毁整个软件专利系统(尽管这不是坏事！)。我们需要先在沙盘中勾勒出JVM等价的轮廓，与图灵等价相比，这更接近于实际需要。依我的观点，无论是细微的二进制格式翻译，甚至于上层的源代码和运行时兼容，都可以明确地将Android置于Java等价的阵线中。<br />
<br />
<strong><span style="font-size: 12pt;">等价的API与运行时环境</span></strong><br />
&nbsp;&nbsp;&nbsp; Android使用了许多JavaSE API的子集。这些(来自于Harmony的)API是全新的实现，但它们将JavaSE作为模型。如果不是由于TCK协议的问题，Harmony甚至会获得JavaSE认证。并不能改变Harmony与JavaSE API是完全等价的这一事实--这是故意为之，并非偶然。正如JRuby名人Charles Nutter最近写到的：<br />
&nbsp;&nbsp;&nbsp; Android支持一个粗糙(但庞大的)Java 1.5类库子集。这个子集足够的大，只需要很少的限制，<strong>就能使得如JRuby这般复杂的项目基本上无需修改就可运行在Android上</strong>。<br />
&nbsp;&nbsp;&nbsp; 看起来，Dalvik足够接近JVM，它应该就完全遵从大部分JVM的规范，包括完整及非常细节的JMM(Android支持Java风格的线程与并发，直至高级的<em>java.util.concurrent</em>包)。有太多"Dalvik是一个新的VM"或"Dalvik不能运行Java类"的话(在讨论该问题的90%的博客和论坛中都会发现这样的言论)。<br />
<br />
<strong><span style="font-size: 12pt;">最后的思考</span></strong><br />
&nbsp;&nbsp;&nbsp; 本篇并不是关于Oracle针对Google诉讼的是非曲直。我将会忽略(可能会删除)任何跑题的评论(不在Android=Java这一论题之内)。对于"Android与Java完全不相干"这一无意义的言论我只能表示厌恶；Google和Android的拥趸们必须找到比这更好的论证。<br />
&nbsp;&nbsp;&nbsp; (<em>当诉讼的全部细节和结果出来之前，对这一诉讼的将来我保留自己的全部判断。除非你有内部消息(我没有)，<strong>请不要太天真</strong>。<a href="http://www.java.net/blog/fabriziogiudici/archive/2010/08/13/oracle-vs-google-stay-cool">保持冷静</a>！我们真的不知道 Oracle或Google的全部意图和计划。自从2007年Google首次发布Android(这导致了JavaME生态系统的极大分裂)，虽然 Sun十分愤怒但仍不得不夹着尾巴，我们不知道这幕后的故事。我不会认同任何价值数以十亿计，且由股东控制的公司所持的利己主义的动机。</em>) <br />
&nbsp;&nbsp;&nbsp; 我不认为Google创建一个基于Java但偏离Java甚远的平台(如.NET所做的那样)是无竞争力的。在保持对已有Java代码和类库巨大兼容性，Java人才及Java工具链之间的平衡性的意愿时，Dalvik和Android框架会让你获得尽可能多的好处。<br />
&nbsp;&nbsp;&nbsp; 显然，Android=Java不能在双方面都包含进来(不是双射)。每个平台都有一些独一无二的 API，当然，Android是一个完整的操作系统，它包括一个基于Linux的内核，图形和电话栈，等等。我显然只谈及普通的部分：基于Java的用户空间/应用框架要依赖Java源代码和类(无论是哪种格式的)，API(包含数以千计的通用JavaSE API)，以及非常引人注目的类Java的虚拟机。对Android与其它Java平台关系的准确描述可能要使用到版本或测评的概念。我记得一个博客说过像"Android没有'J'"这样的话。好的，还不晚：我的建议是将Android的名称修改为Java GE(Java Google Edition)。这就能一劳永逸地消除困惑;-)<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/329582.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2010-08-22 19:49 <a href="http://www.blogjava.net/jiangshachina/archive/2010/08/22/329582.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>