﻿<?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-程序人生-文章分类-Wicked Cool Java中文版：代码、开源类库与项目创意</title><link>http://www.blogjava.net/yydy1983/category/35723.html</link><description>&lt;a href = "http://www.doudou.com/reg.php?comID=4467803" target="_blank"&gt;&lt;img src="http://www.doudou.com/images/top_03.jpg" border="0" /&gt;&lt;/a&gt; &amp;nbsp &amp;nbsp
&lt;a href = "http://www.doudou.com/adLink.php?comID=4467803" target="_blank"&gt;&lt;img src="http://www.doudou.com/images/gamenew1_23.gif" width="151" height="62" border="0" /&gt;&lt;/a&gt;&lt;br&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 17 Dec 2008 19:08:42 GMT</lastBuildDate><pubDate>Wed, 17 Dec 2008 19:08:42 GMT</pubDate><ttl>60</ttl><item><title>2.5 使用正则表达式进行替换</title><link>http://www.blogjava.net/yydy1983/articles/246997.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Wed, 17 Dec 2008 17:01:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/246997.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/246997.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/246997.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/246997.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/246997.html</trackback:ping><description><![CDATA[<p><strong>2.5&nbsp; 使用正则表达式进行替换</strong> <br />
<br />
在上一节中，介绍了用于匹配一个String中的模式和用于从一个子模式组中检索数据的正则表达式。使用regex，还可以用新的值替代匹配的模式。完成此操作的一种方法是使用Matcher类的replaceAll方法，它将返回一个字符串，将所有匹配的子串替换为给定的字符串。为了说明此方法，查找一个文件内出现的所有repetition单词并使用单词duplication来替换它们：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String data = getStringData();<br />
            Pattern repPattern = Pattern.compile("(<a href="file://s)(repetition)(%5B//s;//">\\s)(repetition)([\\s;\\</a>.,])");<br />
            Matcher repMatcher = repPattern.matcher(data);<br />
            String newData = repMatcher.replaceAll("$1duplication$3");  </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>为了查找单词，需要捕获它前后的空白(或标点符号)。需要注意的是，如果此单词出现在data字符串的开头，那么上述的代码不会匹配它，因为已假定它的前后存在空白符。除了单词repetition被替换外，我们希望文本中的其他内容与原始文本保持一致，包括单词周围的空白字符。这里的美元符号($)显然不表示货币。它表示对从regex模式中捕获的组1和组3的逆向引用，包含最初匹配的空白或标点符号。这样，与它们对应的值将插入到替换文本中。</p>
<p>String类(在JDK 1.4或更高的版本中)有一个replaceAll方法，其工作机制类似于Matcher中的replaceAll方法。这使得它可以很方便地替换一个与模式匹配的子串：<br />
</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String data = getStringData();<br />
            String result =<br />
            data.replaceAll("(<a href="file://s)(repetition)(%5B//s;//">\\s)(repetition)([\\s;\\</a>.,])", "$1duplication$3");    </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>replaceAll方法返回一个新的字符串，其中所有匹配的部分已被新值替换。但是，使用Matcher仍有很多优点，因为相对于字符串它有更大的灵活性。</p>
<p>可以使用Matcher的find循环用新值依次替换每个匹配的部分。这样使你能够更好地控制替换过程。比如，可以在每次匹配的过程中应用其他逻辑，甚至每次可以替换不同的值。这就用到了StringBuffer，StringBuffer用于保存更新的文本，并且一旦调用appendReplacement方法，Matcher就追加更新后的文本到缓存中。在处理每个匹配并执行替换后，需要使用appendTail方法将最后一次匹配后的剩余字符串放置在输出缓存中。图2-1说明了子串匹配和这两种方法之间的关系。<br />
</p>
<table class="ln" cellspacing="0" bordercolordark="#ffffff" align="center" bgcolor="#ddddd" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td bgcolor="#ffffff"><a href="http://new.51cto.com/files/uploadimg/20071108/134652727.gif" target="_blank"><img class="fit-image" onmousewheel="javascript:return big(this)" style="zoom: 120%" height="179" alt="" src="http://new.51cto.com/files/uploadimg/20071108/134652727.gif" width="486" onload="javascript:if(this.width />498)this.style.width=498;" border=0></a><a href="http://book.51cto.com/files/uploadimg/20060921/153223104.gif" target="_blank"></a></td>
        </tr>
        <tr>
            <td class="it" align="center" bgcolor="#dddddd">图2-1&nbsp; Matcher类的append方法&nbsp;</td>
        </tr>
    </tbody>
</table>
Matcher有一个相应的append指针。指针最初从零开始，随着每次调用appendReplacement向前移动。这种设计是为了在一个find循环内使用它。每次匹配后，调用appendReplacement方法，Matcher将指针所在的上一个位置到匹配之前指针所在的位置之间的内容，即未更改的文本合并到StringBuffer中。然后，Matcher替换当前匹配的文本并将替换后的内容放置在StringBuffer中。接下来，Matcher将append指针移动到当前匹配结尾之后的第一个字符，然后重复此过程直到不再产生匹配。在找到所有匹配之后很可能剩下一个未匹配的部分。为了将这部分文本添加到输出StringBuffer中，使用appendTail方法。<br />
现在使用这些方法将前面的替换例子重写为一个循环。但是这一次对于每个匹配，将使用一个随机选择的同义词(repetition、duplication、copying、reiteration、recurrence或redundancy)来替代单词repetition：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>StringBuffer result = new StringBuffer();<br />
            String[] wordChoices = new String[]<br />
            {"repetition", "duplication", "copying",<br />
            "reiteration", "recurrence", "redundancy"};<br />
            Random rand = new Random();<br />
            String data = getStringData();<br />
            Pattern repPattern = Pattern.compile("(<a href="file://s)(repetition)(%5B//s;//">\\s)(repetition)([\\s;\\</a>.,])");<br />
            Matcher repMatcher = repPattern.matcher(data);<br />
            while (repMatcher.find()) {<br />
            // pick a word at random<br />
            int wordIndex = rand.nextInt(wordChoices.length);<br />
            String replacement = "$1" + wordChoices[wordIndex] + "$3";<br />
            repMatcher.appendReplacement(result, replacement);<br />
            }<br />
            repMatcher.appendTail(result);<br />
            System.out.println(result);   </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
可以按需求改写find循环中的逻辑来对每个匹配进行所需的处理。此外，还可以使用前面讨论过的Matcher的方法：group、start和end。可以使用这些技术的组合有选择地修改或删除一个文件中每部分匹配的文本。
<img src ="http://www.blogjava.net/yydy1983/aggbug/246997.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-12-18 01:01 <a href="http://www.blogjava.net/yydy1983/articles/246997.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2.4 使用Regex捕获组</title><link>http://www.blogjava.net/yydy1983/articles/245894.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Fri, 12 Dec 2008 03:38:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/245894.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/245894.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/245894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/245894.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/245894.html</trackback:ping><description><![CDATA[<p><strong>2.4&nbsp; 使用Regex捕获组 <br />
</strong>&nbsp;<br />
在上一节中，介绍了如何使用正则表达式在一个文件中进行搜索以便检索它内部所有的URL。可以使用Matcher类的find、start和end方法来检索匹配的URL字符串。有时有必要进一步处理子串匹配的结果，或是查找附加的子模式。例如，对某个特定区域的URL不进行处理。为了实现此目的，一种强制性的方法是使用另一个Pattern和Matcher对象，代码如下：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>// assume urlMatcher instance as in the previous example<br />
            while (urlMatcher.find()) {<br />
            int startIndex = urlMatcher.start();<br />
            int endIndex = urlMatcher.end();<br />
            String currentMatch = data.substring(startIndex, endIndex);<br />
            // the brute force approach, using a new pattern!<br />
            Pattern restricted = Pattern.compile(".*(abc|cbs|nbc)\\.com.*");<br />
            Matcher restrictMatcher = restricted.matcher(currentMatch);<br />
            if (!restrictMatcher.matches()) {<br />
            System.out.println(currentMatch);<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
在捕获的URL中匹配域名并不是一个非常高效的方法。由于已经使用find方法完成了提取URL的困难工作，不应该仅仅为了获得结果的一部分而编写另一个regex，并且也不必这样做。正则表达式允许将模式分解成子序列。使用圆括号，将以后要用到的模式部分括起来，这样，我们就可以忽略其余部分单独读取这些部分的值。重写URL模式以使域名可以与URL的其他部分相分离：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String urlPattern =<br />
            "(http|https|ftp)://([a-zA-Z0-9-\\.]+)[/\\w\\.\\-\\+\\?%=&amp;;:,#]*";</pre>
            </td>
        </tr>
    </tbody>
</table>
当在模式中存在用括号括起来的组时，可以分别检索每个组的匹配值。从最左边的组开始编为1，然后依次对每对括号相对应的组进行编号。在上面的模式中，第一组是协议(如http)，第二组是域名。为了在匹配的字符串中访问组，可以使用Matcher的group方法。下面的代码示例从每个URL中检索域名并显示它们的值：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String data = getStringData(); // load the document<br />
            String urlString =<br />
            "(http|https|ftp)://([a-zA-Z0-9-\\.]+)[/\\w\\.\\-\\+\\?%=&amp;;:,#]*";<br />
            Pattern urlPattern = Pattern.compile(urlString);<br />
            Matcher urlMatcher = urlPattern.matcher(data);<br />
            // print out the domain from each URL<br />
            while (urlMatcher.find()) {<br />
            String domain = urlMatcher.group(2); // 2nd group is the domain<br />
            System.out.println(domain);<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>保存每个匹配的组以便可以随后引用它们。在一个模式内引用一个以前的匹配组称为逆向引用(backreference)。为了对第三个组进行逆向引用，在模式中包括\3即可。这将会只匹配一个与以前的组相匹配的严格重复的数据。为了说明此问题，考虑一个在文本文件中常见的错误—— 一个句子中意外地重复出现某个常用的单词，如&#8220;the&#8221;或&#8220;of&#8221;。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>" The the water molecules are made of of hydrogen and oxygen."</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>下面编写一个模式来找出文件中存在的这些问题。该模式将捕获第一个单词，后跟一些空白符，而其后又跟着匹配第一个单词的重复模式：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String wordPattern = "<a href="file://s(of%7Cor%7Cthe%7Cto)//s+//1%5B//s//">\\s(of|or|the|to)\\s+\\1[\\s\\</a>.,;]";<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
该模式匹配情况如下：一个空白字符、特殊的单词列表中的一个单词、更多的空白、再次重复的相同的单词(使用\1逆向引用)以及空白符或标点符号。这种匹配应不区分大小写，以便能够捕获到&#8220;The the&#8221;以及类似的变型。如以下的代码段所示，该模式不区分大小写，能在一个字符串中查找重复出现的模式：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String data = getStringData();<br />
            String patternStr = "<a href="file://s(of%7Cor%7Cthe%7Cto)//s+//1%5B//s//">\\s(of|or|the|to)\\s+\\1[\\s\\</a>.,;]";<br />
            Pattern wordPattern =<br />
            Pattern.compile(patternStr, Pattern.CASE_INSENSITIVE);<br />
            Matcher wordMatcher = wordPattern.matcher(data);<br />
            while (wordMatcher.find()) {<br />
            int start = wordMatcher.start();<br />
            String word = wordMatcher.group(1);<br />
            // print the index location of the repeated word<br />
            System.out.println("Repeated " + word + " starting at " + start);<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>有一种简便和强大的匹配文件中文本的方法，该方法允许使用多个正则表达式来处理文件，本章后面的&#8220;使用Scanner类进行语法分析&#8221;一节将会讲解此方法。若想了解使用内置索引进行更为复杂的文本搜索的解决方法，请参考第3章中&#8220;使用Lucene进行搜索&#8221;一节的内容。</p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/245894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-12-12 11:38 <a href="http://www.blogjava.net/yydy1983/articles/245894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2.1 使用正则表达式来搜索文本</title><link>http://www.blogjava.net/yydy1983/articles/244067.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Wed, 03 Dec 2008 01:34:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/244067.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/244067.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/244067.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/244067.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/244067.html</trackback:ping><description><![CDATA[<p>很长时间以来，Unix管理用户使用基于正则表达式的命令或程序，如grep、perl、sed和awk。由于这是一个非常强大的搜索和操纵字符串的手段，Java 1.4向核心API添加了java.util.regex程序包。当然，Java是与平台无关的，这些正则表达式可以运行在任何系统上，而不仅仅是Unix。JDK中包含Regex程序包已有一段时间，但是我仍发现很多Java编程人员从未使用过它。Regex模式是一种有价值的基础工具，可以用于很多类型的文本处理，如匹配、搜索、提取、替换和分析结构化内容。 </p>
<p>在Java中，通过使用适当命名的Pattern类可以容易地确定String是否匹配某种模式。模式可以像匹配某个特定的String值那样简单，也可以很复杂，需要采用分组和字符类，如空白、数字、字母或控制符。由于它们是Java字符串并且基于Unicode(统一字符编码)，正则表达式也适用于国际化的应用程序。</p>
<p>正则式是最简单的能准确匹配一个给定String的模式。换句话说，模式与所要匹配的文本是等价的。静态的Pattern.matches方法用于比较一个String是否匹配一个给定模式。 以下的代码将检查变量data中的值是否与单词&#8220;Java&#8221;相匹配： </p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String data = getStringData(); // populate the String somehow<br />
            boolean result = Pattern.matches("Java", data); // is it "Java"?  </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>对于直接匹配字符串和简单的模式而言，你很可能不会使用正则表达式，因为这实际上就是低效率版本的"Java".equals(data)。Regex的真正强大之处体现在使用包括字符类和量词(*、+和?)的更复杂的模式上。目前已有很多优秀的有关正则表达式模式的书籍，因此这里只讨论模式的一些基本特性并重点讨论Java的regex类和方法。为了快速了解这些特性，这里给出一些在正则表达式中使用的特殊字符，其中每一个均代表一类字符，在regex术语中它称为字符类： </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>\d&nbsp;&nbsp;&nbsp;数字 <br />
            \D&nbsp;&nbsp;非数字 <br />
            \w&nbsp;&nbsp;单字字符(0&#8211;9，A&#8211;Z，a&#8211;z，_ ) <br />
            \W&nbsp;&nbsp;非单字字符 <br />
            \s&nbsp;&nbsp;&nbsp;空白(空格符、换行符、回车键、制表符) <br />
            \S&nbsp;&nbsp;&nbsp;非空白 <br />
            [ ]&nbsp;&nbsp;由方括号内的一个字符列表创建的自定义字符类 <br />
            .&nbsp;&nbsp;&nbsp;&nbsp;匹配任何单个字符(除了换行符)  </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>大多数字符在一个模式表达式中代表它们自己，但是一些字符有其特殊含义。上面使用的反斜杠(转义字符)就是一个例子。下面的字符用于控制将一个子模式应用到匹配过程的次数。这些特殊字符的处理方式不同于其他字符：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>? &nbsp;&nbsp;重复前面的子模式零次或一次 <br />
            * &nbsp;&nbsp;重复前面的子模式零次或多次<br />
            + &nbsp;&nbsp;重复前面的子模式一次或多次 </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>下面的正则表达式可以匹配任何对先辈的称呼，如father、great-great-grandmother或great-great-great-grandfather。正如下面的示例所示，通过使用括弧中的子表达式可以创建更复杂的正则表达式：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>((great&#8211;)*grand)?(mother|father)</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>接下来这个模式表达式将会匹配一个以一个数字开头后跟零个或多个非空白字符的任意字符串(例如它将匹配&#8220;3&#8221;、&#8220;5x&#8221;和&#8220;56abcd9&#8221;，而不匹配&#8220;8 5&#8221;或&#8220;hello&#8221;)：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>\d\S*  </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>要谨慎使用regex反斜杠字符，因为在Java中它也是String文字转义字符。如果使用一个String文字来保存正则表达式，将需要通过使用两个反斜杠来转义反斜杠本身。例如： </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String digitNonSpacePattern = "<a href="file://d//S">\\d\\S</a>*";<br />
            String data = getStringData();<br />
            boolean isMatch = Pattern.matches(digitNonSpacePattern, data);  </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>此外，模式匹配也内置于String类本身。在String类中有一个新的简便方法：matches。可以如下重写以上的代码：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>boolean isMatch = getStringData().matches("<a href="file://d//S">\\d\\S</a>*"); </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>Pattern.matches方法和String的matches方法都适合一次性使用，但是若重复使用它们的效率较低。通过使用静态的Pattern.compile方法创建一个Pattern实例可以得到一个更高效的用于执行多次匹配的编译版本的模式。Pattern对象可以和java.util.regex.Matcher类协同工作。为了执行复杂的匹配，需要创建Matcher(匹配器)。Matcher将一个模式表达式绑定到一个特定的字符串，以便执行更高级的匹配操作。以下的代码段编译了一个匹配仅由单个字符组成的模式：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String data = getStringData();<br />
            Pattern namePattern = Pattern.compile("<a href="file://w/">\\w</a>+");<br />
            // get a Matcher to apply the pattern to the data<br />
            Matcher nameMatcher = namePattern.matcher(data);<br />
            boolean isMatch = nameMatcher.matches(); </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>要记住matches方法将会对整个输入字符串和模式进行匹配。如果希望检查字符串是否仅以模式开头，则可以使用lookingAt方法： </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>boolean startsWith = nameMatcher.lookingAt(); </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>在以下几个小节中将会讨论其他一些匹配技术，包括查找匹配一个模式的子串以及执行文本替换。 </p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/244067.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-12-03 09:34 <a href="http://www.blogjava.net/yydy1983/articles/244067.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.10 “==”不等于“.equals”</title><link>http://www.blogjava.net/yydy1983/articles/java.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Wed, 26 Nov 2008 00:55:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/java.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/242711.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/java.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/242711.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/242711.html</trackback:ping><description><![CDATA[<p><strong>1.10&nbsp; &#8220;==&#8221;不等于&#8220;.equals&#8221;</strong></p>
<p>这里举出一个Java编程程序员经常碰到的问题。例如现在是凌晨3点，在你喝完第4杯咖啡后，你设法找到正确的逻辑来解决复杂的编程问题。到目前，你几乎不能思考String和Object引用，因为你已经昏昏欲睡了。然后糟糕的事情发生了&#8230;&#8230;不，并不是Java溢出，而是如下所示。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String name = getName();<br />
            if (name == "Sleepy") // oops!<br />
            {<br />
            doSomething();<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>你快速编译并测试代码后，代码似乎正常运行。终于到下班回家休息的时候了！然而，一段时间后，应用程序测试发现了一个间歇性错误，并跟踪到此错误的来源恰好是这段代码。</p>
<p>&#8220;怎么会这样？&#8221;你可能会愤怒地说，&#8220;前几天我还试验过类似的String比较，并且能够正确运行！&#8221;。但是，你需要首先重温一下Java对象引用的概念。一个对象变量是一个指向存储在堆内存(heap memory)中实际对象的引用(指针)。当为另一个变量分配一个变量时，事实上分配的是引用而不是实际的对象(如图1-1所示)：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String a, b, c, d;<br />
            a = "123";<br />
            b = a;<br />
            c = new String("123");<br />
            d = "WCJ";<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<table class="ln" cellspacing="0" bordercolordark="#ffffff" align="center" bgcolor="#ddddd" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td bgcolor="#ffffff"><a href="http://new.51cto.com/files/uploadimg/20071108/112525487.gif" target="_blank"><img class="fit-image" onmousewheel="javascript:return big(this)" style="zoom: 70%" height="545" alt="" src="http://new.51cto.com/files/uploadimg/20071108/112525487.gif" width="441" onload="javascript:if(this.width />498)this.style.width=498;" border=0></a><a href="http://book.51cto.com/files/uploadimg/20060921/153223104.gif" target="_blank"></a></td>
        </tr>
        <tr>
            <td class="it" align="center" bgcolor="#dddddd">图 1-1&nbsp; 对象引用</td>
        </tr>
    </tbody>
</table>
<p>Java中，&#8220;==&#8221;运算符用来比较两个引用以查看它们是否指向同一个内存对象。而对于String实例，运行时状态会尽可能地确保任意两个具有相同字符信息的String字面值指向同一个内部对象。此过程称为驻留(interning)，但是它并不有助于每个String的比较。一个原因是垃圾收集器线程删除了驻留值，另一个原因是String所在的位置可能被一个由String构造函数创建的新实例占用。如果是这样，&#8220;==&#8221;将总是返回false。<br />
可以设计equals方法来比较两个对象的状态(state)或每个对象的内容。对你自己的类，必须重写此方法来使它正确操作。但是如果使用equals方法，String实例总是能够正确地比较。假定所有的String值是驻留的，下面的代码段说明了此问题： </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String name1, name2, name3;<br />
            name1 = "123";
            <p>name2 = name1;<br />
            if (name1 == name2) {} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// true<br />
            if (name1.equals(name2)) {} &nbsp;&nbsp;&nbsp;// true</p>
            <p>name2 = "123";<br />
            if (name1 == name2) {} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// usually true<br />
            if (name1.equals(name2)) {} &nbsp;&nbsp;&nbsp;// true</p>
            <p>name3 = new String("123");<br />
            if (name1 == name3) {} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// false<br />
            if (name1.equals(name3)) {} &nbsp;&nbsp;&nbsp;// true<br />
            </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br />
<strong>注意：</strong></p>
<p>总是使用.equals来比较两个String值，尽管使用&#8220;==&#8221;运算符看似能够正确操作。对于大多数应用程序而言，即使它能正确运行，但&#8220;==&#8221;代码事实上是错误的，而只有equals是正确的。因此告诉所有你的开发同行支持String的&#8220;equals(平等)&#8221;权吧(这很可能是本书中最差的双关语)！ </p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/242711.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-26 08:55 <a href="http://www.blogjava.net/yydy1983/articles/java.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.9 创建一个匿名的类</title><link>http://www.blogjava.net/yydy1983/articles/241727.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Thu, 20 Nov 2008 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/241727.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/241727.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/241727.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/241727.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/241727.html</trackback:ping><description><![CDATA[<p><strong>1.9&nbsp; 创建一个匿名的类 </strong></p>
<p><strong>Java1.1+</strong></p>
<p>当进行Java开发时，有时需要实现一个仅包含1~2个方法的接口，并且每个方法只有1~2行代码。在AWT和Swing开发中经常会出现这种情况，例如当一个display组件需要一个事件回调方法(如一个按钮的ActionListener)时。如果使用普通的类来实现此操作，最终会得到很多仅在单个位置上使用的小型类。其实，Java允许定义内部类，而且可以在GUI架构外使用内部类。</p>
<p>内部类(inner class)是指在另一个类内部定义的一个类。可以将内部类定义为一个类的成员，如下例所示：</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class Linker {<br />
            public class LinkedNode {<br />
            LinkedNode prev, next;<br />
            Object contents;<br />
            }
            <p>public Linker() {<br />
            LinkedNode first = new LinkedNode();<br />
            LinkedNode second = new LinkedNode();<br />
            first.next = second;<br />
            first.contents = "This is the first item";<br />
            second.prev = first;<br />
            second.contents = "This is the second item";<br />
            }<br />
            }   </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>这是一个在Java中实现的简单链表。LinkedNode类是Linker类的内部类。尽管此类包含在Linker类中，仍可以从其他类访问它，因为将它定义为公有的(public)。通过使用new Linker.LinkedNode()，可以从其他类创建它的一个实例。源自核心API的Map.Entry类就是这样一个类，当在一个映射表中检索&#8220;键/值&#8221;表项的集合时，java.util.Map类会用到此类。</p>
<p>此外，还可以定义一个局限于一个方法的内部类。这种类型的类只在定义它的方法内是可见的。如果要实现一个接口并且希望在方法内不止一次地使用局部类，那么很可能这样做，如下面这个实例所示：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class Happiness {<br />
            interface Smiler { public void smile(); }
            <p>public static void main(String[] args)<br />
            {<br />
            class Happy implements Smiler {<br />
            private String more = "";<br />
            public void smile() {<br />
            System.out.println(":-)" + more);<br />
            }<br />
            public void happier() {<br />
            more += ")";<br />
            }<br />
            }</p>
            <p>Happy h1 = new Happy();<br />
            h1.smile();<br />
            Happy h2 = new Happy();<br />
            h2.happier();<br />
            h2.smile();<br />
            }<br />
            } </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
局部内部类的名称为Happy(我喜欢把它看作内部者的类)。它不能从其他任何类或方法中访问。使用这个局部类是为了实现Smiler接口(它是在Happiness内部定义的)。对于很多情况而言，甚至不需要为一个局部内部类命名—— 它可以保持为匿名的。对于实现仅有1~2个方法的接口而言匿名类非常有用，尤其是当需要创建一个仅使用一次的实例的时候。通常情况下不能直接实例化一个接口。例如，不能调用Runnable接口的构造函数(这是由Thread使用的接口)。下面的做法是非法的：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Runnable runner = new Runnable(); // not allowed!  </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
但是，Java确实允许创建一个用于实现接口的匿名局部类的实例。可以使用一种特殊的语法来创建一个接口的实例：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Runnable runner = new Runnable() {<br />
            public void run() {<br />
            for (int i=0; i&lt;10000000; i++) {<br />
            countSheep();<br />
            }<br />
            }<br />
            };</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
此代码创建了一个局部匿名类，实例化该类并为runner变量分配新的实例。需要确保你的类定义(外层花括号之间的代码)实现了接口的所有方法。此外，还可以按照这种方式创建子类。下面的代码实例化Object的一个匿名子类并将它分配给一个变量：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Object timePrinter = new Object() {<br />
            public String toString() {<br />
            return String.valueOf(System.currentTimeMillis());<br />
            }<br />
            };<br />
            System.out.println(timePrinter); </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
匿名类是Java开发人员常用的一个有用工具。无论何时需要一次性地实现一个接口(或子类)，都可以考虑使用这些类。但最好将它们用于仅有一到两个方法的小型接口。<br />
</p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/241727.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-20 17:50 <a href="http://www.blogjava.net/yydy1983/articles/241727.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.8 亚毫秒级的线程休眠</title><link>http://www.blogjava.net/yydy1983/articles/241327.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Wed, 19 Nov 2008 05:03:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/241327.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/241327.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/241327.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/241327.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/241327.html</trackback:ping><description><![CDATA[<p><strong>1.8&nbsp; 亚毫秒级的线程休眠</strong></p>
<p><font face="楷体_GB2312">Java1.1+</font></p>
<p>正如上一节中所讨论的，Java 5在System类中添加了一个nanoTime方法来确保时间度量能够适应更快的系统。即使在早期版本的Java中，线程可以具有少于1毫秒的休眠时间。回想一下Java的线程机制有一个sleep方法，该方法采用一个int参数来表示以毫秒计量的休眠时间。此外还有一个sleep方法，它接受一个毫秒参数和一个以纳秒计量的时间参数。如果将毫秒时间设为零，那么线程将会休眠指定的纳秒(ns)数。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class MyThread extends Thread {<br />
            public void run() {<br />
            try {<br />
            sleep(10); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// sleep for 10 milliseconds<br />
            sleep(0, 10000); &nbsp;&nbsp;&nbsp;// sleep for 0.01 milliseconds (10000 ns)<br />
            } catch (InterruptedException e) {<br />
            e.printStackTrace();<br />
            }<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
就像前面介绍的nanoTime方法一样，该方法存在同样的问题。由于操作系统特性、机器处理速度和系统负载的不同，实际的休眠时间可能与期望的不同。</p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/241327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-19 13:03 <a href="http://www.blogjava.net/yydy1983/articles/241327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.7 以纳秒级的时间计算：使用System.nanoTime</title><link>http://www.blogjava.net/yydy1983/articles/241172.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Tue, 18 Nov 2008 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/241172.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/241172.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/241172.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/241172.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/241172.html</trackback:ping><description><![CDATA[<p><strong>1.7&nbsp; 以纳秒级的时间计算：使用System.nanoTime</strong>&nbsp;</p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>摩尔定律是一种众所周知的现象，即计算机中的晶体管数量和它的处理速度随时间呈指数规律增长。作为仙童半导体公司(Fairchild Semiconductor)的研发领导人，戈登&#8226;摩尔于1965年提出了这一伟大发现。迄今为止，它仍有效。<br />
与Java首次出现的时候相比，当前计算机的速度要快得多，对于很多应用程序而言以毫秒计时已不再能够满足要求。你可能使用过java.lang.System类，利用currentTimeMillis方法来获得一个方法调用或一段代码的定时信息。此方法可以用来度量执行某操作所花费的时间。但是，在运算速度更快的计算机上操作花费的时间可能远小于1毫秒，于是可以在一个for循环中执行此操作上百次或上千次，然后除以循环次数来计算此操作的单位时间。考虑下面的示例：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>long startTime = System.currentTimeMillis();<br />
            for (int i=0; i&lt;1000; i++) {<br />
            performOperation(); // something we want to measure<br />
            }<br />
            long endTime = System.currentTimeMillis();<br />
            long totalTimeInMillis = endTime - startTime;<br />
            // because the count was 1000, it's easy to get the unit time<br />
            long unitTimeInMicros = totalTimeInMillis;</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>这种一种很简单的运算，因为使用了for循环1000次。但是如果要度量亚微秒该如何实现呢？</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>for(int i=0; i&lt;1000000; i++) { performOperation(); }</pre>
            </td>
        </tr>
    </tbody>
</table>
如果从人类的角度来看，可怜的for循环将不得不不厌其烦地百万次的频繁循环！此外，只有在重复执行操作没有副作用的情况下使用for循环来计算时间才是有用的。如果操作是调用java.util.Collections.sort方法，那么将很难计算出排序过程花费的时间。在Java 5中，System类有一个新的nanoTime方法，它能返回一个纳秒精度的计数器。尽管不能将它用于度量绝对时间，但是它能够很好地度量时间差别。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>List myList = initializeList(); &nbsp;// initialize the List somehow<br />
            long startTime = System.nanoTime();<br />
            Collections.sort(myList); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// measuring the sort time<br />
            long endTime = System.nanoTime();<br />
            long differenceInNanoseconds = endTime - startTime;</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>遗憾的是，运行上面的代码时无法保证实际上获得的是纳秒级的度量。但是使用更快的机器和良好的JRE实现，对于测试目的而言它是一种有用的度量方法。可以在JDK 5文档中找到更多有关此方法的信息。鉴于操作系统特性、机器处理速度和系统负载的不同，得到的由nanoTime方法返回的值可能会有很大的变化。随着时间的推移此问题应该会有所改善，摩尔定律基本上能保证这一点。</p>
<p><font face="楷体_GB2312">参考资料：</font></p>
<p>想要了解摩尔的原始论文，请参看Gordon E. Moore, Cramming More Components onto Integrated Circuits, Electronics, Vol. 38, No. 8 (April 19, 1965)。此外，还可以在网上获得该论文，参看本书的网站http:// wickedcooljava.com以获得URL。</p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/241172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-18 16:57 <a href="http://www.blogjava.net/yydy1983/articles/241172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.6 要决断：使用Java断言</title><link>http://www.blogjava.net/yydy1983/articles/240957.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Mon, 17 Nov 2008 06:29:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/240957.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/240957.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/240957.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/240957.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/240957.html</trackback:ping><description><![CDATA[<p><strong>1.6&nbsp; 要决断：使用Java断言</strong>&nbsp;</p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>&#8220;编程人员总是正确的—— 是编译器和解释器造成的错误。&#8221;我确信你认同这种说法。作为编程人员，经常要对变量的值做出假设并且基于此编写代码。尽管非常不愿意承认可能在设计或实现上有错误，但有时变量和参数却没有获得期望的值。</p>
<p>当设计和编写代码时，只有在最初的假设仍然成立的情况下代码才能正确运行。如果没有任何有关这些假设的声明，那么阅读代码的任何人(甚至你自己)都不清楚它们的含义是什么。从而导致今后的改动可能会违反这些假设并引入难以查找的错误。通常，在注释中说明假设能使以后修改代码的人避免出错。</p>
<p>使用注释来说明假设是一个好的开始。但是当出现违反假设的情况时，程序有时会继续运行就像未出现任何问题一样。一些情况下，开发人员能够马上看到结果，并且可以纠正出现的问题。但在另一些情况下，存在一个潜伏的错误时，可能会对应用程序的其他部分造成负面影响，对于分布式系统而言，则可能会对完全不同的另一个应用程序造成负面影响！跟踪这样的问题非常困难。</p>
<p>Java 1.4在语言中添加了断言特性来简化测试和调试，加强文档编制并提高基于Java的可维护性。可以使用一个布尔表达式来创建一个断言，以便测试有关系统的当前状态所假定的某些情况。如果断言失败，运行库会抛出一个AssertionError。下面给出一个很简单的断言：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>String name = "Brian";<br />
            assert name != null;</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>这里，可以确定在给name分配了值&#8220;Brian&#8221;后它的值将不会为空。如果它的值为空，则出现了某种严重的错误！此断言是当时在程序中对变量的值所做的假定的声明。为如此简单的例子做这种声明看似可笑和多余。但是，当多个方法会影响一个对象的状态时，这种方法是有效的。在下面的示例中，示例了这样一个断言，即在向新员工分配任何任务之前必须已经指派了一名管理人员。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Employee worker =<br />
            new Employee("John", "Smith", 100000, "Developer");<br />
            assignOffice(worker);<br />
            setUpVoiceMail(worker);<br />
            moreAdministrivia(worker);<br />
            assert worker.getSupervisor() != null : "Supervisor cannot be null";<br />
            assignTasks(worker);</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>通常，在对某个对象执行关键操作时会需要对它创建断言。这有助于增强代码的健壮性，比如如果在程序中出现了某种错误，可以更方便地调试程序。这样做要比程序在某处执行失败造成不良后果来发现错误要好得多。当知道程序失败是由于它违反了假设而引起的时候，跟踪失败的原因要简单得多。在这个代码样例中，使用了assert选项来返回更有用的信息。没有此选项时，除了行号之外将无法得到有关断言的标识信息。 </p>
<p>在某些版本的编译器上，当编译源代码时需要使用一个命令选项来设置编译器的源兼容性模式(取决于编译器的版本，如1.4或1.5)。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>javac -source 1.5 MyClass.java</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>现在强制断言失败并观察会出现什么情况：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class AssertBad {<br />
            public static void main(String[] args) {<br />
            int total = 20;<br />
            int itemCount = 0;<br />
            assert itemCount &gt; 0;<br />
            int average = total / itemCount;<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br />
默认情况下，运行时环境不支持断言，必须使用ea(允许断言)命令选项来启动JRE。上面的代码会引起以下的结果：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>C:\projects\wcj1&gt; java -ea AssertBad<br />
            Exception in thread "main" java.lang.AssertionError<br />
            at AssertBad.main(AssertBad.java:12)</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br />
要记住断言是用来对那些不应该出现的情况进行实际的&#8220;健全性检查&#8221;，因此不应该使用它们来替代常规的错误检查。</p>
<p><strong>警告：</strong><br />
不要让断言语句更改代码中的状态/值。否则当最终关闭断言时，代码的行为方式将不同于启用断言时代码的行为。例如，不要创建如下的断言：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>assert (++i &gt; 10); // BAD: i changes only with assertions enabled!</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>通常，在整个开发阶段都会启用断言。一旦完全测试了系统并将它移送到产品环境时，则希望禁用断言，因为这样做会略微改善性能。但是不要改动代码来完成此操作，并且也不要删除断言。不管怎样，为了编制文档的目的，断言也应保留在代码中。这样，当以后更改代码时，会提醒程序员要保持所有假设都是有效的，并且这也是可测试的。<br />
</p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/240957.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-17 14:29 <a href="http://www.blogjava.net/yydy1983/articles/240957.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.5 使用多个参数：编写Vararg方法</title><link>http://www.blogjava.net/yydy1983/articles/240701.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Sat, 15 Nov 2008 09:42:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/240701.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/240701.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/240701.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/240701.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/240701.html</trackback:ping><description><![CDATA[<p>1.5&nbsp; 使用多个参数：编写Vararg方法 </p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>Java编程人员常常需要为方法定义一个包含多个值的参数。这时可以采用List或数组的形式，如下例所示。<br />
</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public int add(int[] list) {<br />
            int sum = 0;<br />
            for (int i=0; i &lt; list.length; i++) {<br />
            sum += list[i];<br />
            }<br />
            return sum;<br />
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br />
也可以将它实现为几个重载的方法，每个方法接受不同数量的int参数。这样做有时可以令方法更容易使用，因为调用代码不需要首先创建一个数组。</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public int add(int a, int b) {<br />
            return a + b;<br />
            }
            <p>public int add(int a, int b, int c) {<br />
            return a + b + c;<br />
            }</p>
            <p>public int add(int a, int b, int c, int d) {<br />
            return a + b + c + d;<br />
            }<br />
            </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br />
如果只有少量值，这对于调用代码更为方便，因为现在可以使用add(12,14,16)来代替add(new int[] {12,14,16})。但是，编写类似这样的方法存在问题，需要为每种可能的参数组合编写不同版本的方法。如果希望在方法的使用方式上具有最大的灵活性则与其创建一个庞大的具有上千个方法的类，不如对重载的方法接受的参数数量进行很小的限制。</p>
<p>在Java 5中，可以编写一个方法以使它允许可变数量的参数并让编译器完成将列表包装到一个数组中的操作。虽然内部仍是处理数组，但此时的编译器已隐藏了细节。以下的代码使用可变参数(vararg)重写了add方法。 </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public int add(int... list) {<br />
            int sum = 0;<br />
            for (int item : list) {<br />
            sum += item;<br />
            }<br />
            return sum;<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>注意，那些奇怪的圆点正是可变参数的实际语法！并且，由于这种改动要用到Java 5，我们也可以趁机使用Java 5增强的for循环语法。一旦按照这种方式编写了方法，可以使用实际数量的参数来调用它！此外，还可以传递一个作为参数的数组(但不允许是List或Collection对象)： </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>add(1,3,5,7,9,11,13,15,17,19,21,23,25);<br />
            add(new int[] {12,14,16});</pre>
            </td>
        </tr>
    </tbody>
</table>
需要注意的是当定义一个参数列表时该语法只能使用一次，并且它必须是最后一项。以下的代码不能正常运行，因为可变参数不是最后一项：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public void badMethod(int... data, String comment) { } // wrong!</pre>
            </td>
        </tr>
    </tbody>
</table>
必须将此代码改写成以下形式：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public void goodMethod(String comment, int... data) { }</pre>
            </td>
        </tr>
    </tbody>
</table>
在一些情况下，在方法定义中使用可变参数可以使方法更简便。这在参数列表包含字面(硬编码)值的情况下尤其如此，如上面的示例中给出的int值。</p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/240701.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-15 17:42 <a href="http://www.blogjava.net/yydy1983/articles/240701.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.4 常用的泛型：使用泛型参数来编写方法</title><link>http://www.blogjava.net/yydy1983/articles/239780.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Tue, 11 Nov 2008 01:06:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/239780.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/239780.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/239780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/239780.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/239780.html</trackback:ping><description><![CDATA[<p><strong>1.4&nbsp; 常用的泛型：使用泛型参数来编写方法</strong>&nbsp;</p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>前面的小节介绍了泛型可以简化Java代码并使代码能够防范ClassCastException错误。除了作为JDK的一部分来使用泛型之外，还可以编写你自己的泛型。当对类型相同的对象进行操作时泛型是很有用的，但是对象的具体类型直到对类实例化时才能知道。这种方式非常适合于包含关联项目的集合或涉及查找的类。</p>
<p>下面编写一个使用泛型参数的方法。回想一下前面是怎样使用ArrayList类的—— 只在构造ArrayList时才指定它使用哪些对象类型。注意，在定义类时并不知道其类型，并且不能将java.lang.Object作为类型使用，因为最后将遇到类似以前的类型强制转换问题。当定义泛型时，必须使用一种特殊的语言来代表类型。当声明类名时要完成此操作。在下面的示例中，&lt;T&gt;表示一种类将使用的类型：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>&nbsp;public class RandomSelection&lt;T&gt; { }</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>这里，类型指示符的尖括号看起来类似HTML语法，但实际上和HTML没有关系，它们也不表示小于或大于！尖括号在一个泛型的类名与一个类型相结合的情况下使用，正如前面的ArrayList&lt;Integer&gt;那样。尽管直到调用构造函数时才知道真实的类型，但我们可以在方法定义中使用替换类型。假定定义了一个叫做RandomSelection的类，该类使用另一个类的某个类型，暂时将该类型称为T。但是，此类的名字仍是RandomSelection。另外，每次可以对多个类型执行这种操作，正如java.util.Map的定义所示的那样。在这种情况下，在类名之后使用一个由逗号分隔的标识符列表即可。 </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class MyGeneric&lt;T,U,V&gt; { }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>上面定义的MyGeneric类涉及到三个类型，它们分别称为T、U和V。下面编写一个方法来扩展RandomSelection类，该方法将一个项目添加到一个由内部管理的泛型(类型为T)的ArrayList中：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class RandomSelection&lt;T&gt; {<br />
            private ArrayList&lt;T&gt; list;
            <p>public RandomSelection() {<br />
            list = new ArrayList&lt;T&gt;();<br />
            }</p>
            <p>public void add(T element) {<br />
            list.add(element);<br />
            }<br />
            }<br />
            </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>注意，实际上并不是处理一个叫作T的类。T代表当某人创建RandomSelection的一个实例时使用的任意类。Java规范允许使用任意标识符，但是一般是使用单个大写字母来和普通的类名进行区别。既然已经定义add方法接受一个类型T参数，则只能使用在构造RandomSelection实例时采用的相同的类型来调用此方法。以下的代码是非法的并会产生一个编译错误：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>RandomSelection&lt;String&gt; rs = new RandomSelection&lt;String&gt;();<br />
            rs.add(new Date()); // illegal for a RandomSelection&lt;String&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
如果希望一个方法返回一个泛型类型，可以将方法签名的返回类型设为T，正如下面的定义所示：<br />
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>import java.util.Random;<br />
            public class RandomSelection&lt;T&gt; {<br />
            private java.util.Random random = new Random();<br />
            // ..... earlier methods omitted
            <p>public T getRandomElement() {<br />
            int index = random.nextInt(list.size());<br />
            return list.get(index);<br />
            }<br />
            }</p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
getRandomElement方法返回一个类型T，即与在类声明中定义的类型相同。通过构造一个类型实例，现在可以使用刚才定义的RandomSelection类：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>RandomSelection&lt;Integer&gt; selector = new RandomSelection&lt;Integer&gt;();<br />
            selector.add(2);<br />
            selector.add(3);<br />
            selector.add(5);<br />
            selector.add(7);<br />
            selector.add(11);<br />
            Integer choice = selector.getRandomElement();<br />
            System.out.println(choice);</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
给一个整型变量choice赋值是安全的，因为selector的getRandomElement方法返回的总是一个Integer。情况确实如此，因为是使用Integer作为泛型类型来构造的selector实例。Add和getRandomElement方法的定义具有和构造函数的定义相同的类型，并且编译器将会强制执行此约束。尝试在构造函数中以一个不同的类型来使用RandomSelection类，这次使用前面定义的Fruit enum类：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>RandomSelection&lt;Fruit&gt; fruitSelector = new RandomSelection&lt;Fruit&gt;();<br />
            fruitSelector.add(Fruit.APPLE);<br />
            fruitSelector.add(Fruit.ORANGE);<br />
            fruitSelector.add(Fruit.GRAPEFRUIT);<br />
            fruitSelector.add(Fruit.BANANA);<br />
            fruitSelector.add(Fruit.DURIAN);<br />
            Fruit fruitChoice = fruitSelector.getRandomElement();<br />
            System.out.println(fruitChoice);</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
可以看出，能够直接使用来自getRandomElement方法的Fruit返回值，正如前面对待Integer那样。如果你想要一个类与某种类型(直到构造该类时才知道具体的类型)的对象协同操作以及希望编译器严格执行类型限制，那么你可以定义自己的泛型。这样做的主要优点体现在它的安全和便利性上。若想了解有关泛型的更多信息，请查阅网址http:// java.sun.com/j2se/1.5.0/docs/guide/language/generics.html上的Generics Tutorial(泛型指南) 和Java 5文档。 </p>
<img src ="http://www.blogjava.net/yydy1983/aggbug/239780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-11 09:06 <a href="http://www.blogjava.net/yydy1983/articles/239780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.3 执行安全存放：使用类型安全映射</title><link>http://www.blogjava.net/yydy1983/articles/238273.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Mon, 03 Nov 2008 01:10:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/238273.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/238273.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/238273.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/238273.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/238273.html</trackback:ping><description><![CDATA[<p><strong>1.3&nbsp; 执行安全存放：使用类型安全映射</strong></p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>正如前面讨论for循环时看到的那样，使用泛型有助于简化代码并降低出错概率。for循环会假定ArrayList仅包含Integer对象，因为ArrayList严格地被定义为由Integer对象组成。因而当从列表检索项目时可以避免从Object到Integer的强制类型转换。</p>
<p>Java 5对核心API做出了很多利用泛型的更改。查看相关文档你会发现已重新定义了很多类以允许使用泛型。如果愿意的话，仍可以按照以前的方式构造和使用这些类，例如使用new ArrayList()。这样做的原因是为了兼容性，以便仍可以在旧版本的编译器下运行代码。当然，这样会失去泛型提供的类型检查带来的便利性和安全性。</p>
<p>一个得到很好修订的类是java.util.Map (和HashMap)。我们知道，映射操作就像查表一样，每个值都存储在一个唯一的键标下。在早期的Java版本中，当在映射表中放置表项时，它们是作为Object项存放的。当从映射表中检索表项时，它被作为标准的Object引用来对待，即被强制转换成正确的子类以便能够识别为它的实际类型。这与List中存在的危险相同。要么对象不正确，要么出现ClassCastException异常，这样的情况太常见了。</p>
<p>假定有一个用于维护员工数据的Employee类。下面给出一些使用HashMap的典型代码： </p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Employee brian = new Employee();<br />
            brian.setName("Brian", "Eubanks");<br />
            brian.setSalary(100000.00);<br />
            brian.setTitle("Boss");
            <p>HashMap employees = new HashMap();<br />
            employees.put("Brian", brian);</p>
            <p>Employee newHire = (Employee) employees.get("Brian");<br />
            newHire.setHireDate(new Date());<br />
            </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>在检索项目时，最大的危险位于强制类型转换的过程中。使用Java 5，不用强制类型转换也可完成此操作，只要使用正确的类型来实例化Map。可以对键和值的类型添加约束。在下面的示例中，只允许String键和Employee值：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Employee brian = new Employee();<br />
            brian.setName("Brian", "Eubanks");<br />
            brian.setSalary(100000.00);<br />
            brian.setTitle("Boss");
            <p>HashMap&lt;String,Employee&gt; employees = new HashMap&lt;String,Employee&gt;();<br />
            employees.put("Brian", brian);</p>
            <p>// no cast is necessary here<br />
            Employee newHire = employees.get("Brian");<br />
            newHire.setHireDate(new Date());</p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
通过使用类型安全映射，当从映射表中检索表项时可以避免ClassCastException错误。这样做使代码更加稳定并且降低对映射内容的敏感性。但如果代码必须运行在早期版本的Java上，那么将处于不利的情况并且需要暂时继续执行强制类型转换。但可以采用下一节中的方法，创建自己的泛型类。</p>
  <img src ="http://www.blogjava.net/yydy1983/aggbug/238273.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-11-03 09:10 <a href="http://www.blogjava.net/yydy1983/articles/238273.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.2 计数：使用枚举</title><link>http://www.blogjava.net/yydy1983/articles/221618.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Wed, 13 Aug 2008 02:06:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/221618.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/221618.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/221618.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/221618.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/221618.html</trackback:ping><description><![CDATA[<p><strong>1.2&nbsp; 计数：使用枚举</strong>&nbsp;</p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>大多数应用程序需要记录一个值的有限集—— 即应用程序中表示一组选择或状态的常量。一种常见的Java编程惯例是使用static int变量来表示这些值。然后让程序通过比较这些值和其他变量的值来做出决定。尽管核心Java API本身也采用这种惯例，但它很可能导致严重的问题！下面是一个有关水果信息的示例。该示例给出了使用int变量来表示枚举数据时会出现的一些问题。
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public class FruitConstants {<br />
            // this is not such a good practice<br />
            public static final int APPLE = 1;<br />
            public static final int ORANGE = 2;<br />
            public static final int GRAPEFRUIT = 3;<br />
            public static final int BANANA = 4;<br />
            public static final int DURIAN = 5;<br />
            public static final int CITRUS = 6;<br />
            public static final int SWEET = 7;<br />
            public static final int SMELLY = 8;<br />
            public static final int UNKNOWN = 9;
            <p>public static int getCategory(int fruit) {<br />
            switch(fruit) {<br />
            case APPLE: case BANANA:<br />
            return SWEET;<br />
            case ORANGE: case GRAPEFRUIT:<br />
            return CITRUS;<br />
            case DURIAN:<br />
            return SMELLY;<br />
            }<br />
            return UNKNOWN;<br />
            }<br />
            }</p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
其中，来自东南亚的水果榴莲(Durian)具备&#8220;Sweet&#8221;和&#8220;Smelly&#8221;，但是这里的水果只能返回一个特点，也就是说，所有的值只能是对应单一的值而不是对应多个值。另外一个主要的问题是任何int值都可以传递给getCategory方法，无论它是否代表一个有效的水果。这可能导致细微的错误，因为编译器不关心是调用getCategory(SWEET)还是调用getCategory(42)。并且如果整型常量的值发生了变化，getCategory(3)显示的将不再是正确的信息！<br />
另一个问题是使用水果和类别的int值并无区别—— 它们都只是普通的int值。通过简单地将类别常量置于一个不同的类中可以部分地解决水果和类别分离的问题，但是它们仍只是int值而不是类型安全(typesafe)的，也就是没能将getCategory的参数限制到一个固定的值集合。<br />
在Java 5中，有一个优雅的解决方法：可以像在C语言中那样创建枚举类型。这是一个新特性，它创建一个类，该类包含一个所有它允许的实例清单。除了enum内定义的实例外，不允许其他的实例。下面看一些enum的示例：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>enum Fruit {APPLE, ORANGE, GRAPEFRUIT, BANANA, DURIAN}<br />
            enum FruitCategory {SWEET, CITRUS, SMELLY, UNKNOWN}<br />
            enum Dessert {PIE, CAKE, ICECREAM, BROWNIE}</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
以上每个例子均定义了一组不同的枚举元素(选择)。这样做的好处是不会将Fruit值与其他类型的相混合或相混淆。对待每个enum就好像它是一个不同的类一样。不能将FruitCategory作为一个参数传递给一个期望Dessert的值方法，也不能传递一个int值。下面扩展Fruit enum以包含最初的FruitConstants类具有的功能：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public enum Fruit {<br />
            APPLE, ORANGE, GRAPEFRUIT, BANANA, DURIAN;
            <p>public static FruitCategory getCategory(Fruit fruit) {<br />
            switch(fruit) {<br />
            case APPLE: case BANANA:<br />
            return FruitCategory.SWEET;<br />
            case ORANGE: case GRAPEFRUIT:<br />
            return FruitCategory.CITRUS;<br />
            case DURIAN:<br />
            return FruitCategory.SMELLY;<br />
            }<br />
            return FruitCategory.UNKNOWN;<br />
            }<br />
            }</p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
可以看出一个enmu也可以像一个类那样定义方法。现在的getCategory方法采用Fruit作为参数，并且只允许使用enum中定义的值。接下来这个代码段将引起编译错误，而不会出现调用最初的未受保护的getCategory方法时出现的运行时异常：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>Fruit.getCategory(Dessert.PIE); // compile error<br />
            Fruit.getCategory(10); // compile error</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
如果每种水果管理它自己的类别将会更好，因此为了完善Fruit类，将删除getCategory的水果参数并使该方法为每个enum状态返回不同的值。通过创建一个适用于所有值的抽象的getCategory方法并对每个enum以不同的方式重写它，可以完成此操作。它非常类似于为每个枚举值编写一个不同的子类，并让每个这样的子类重写抽象的方法。
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public enum Fruit {<br />
            APPLE<br />
            { FruitCategory getCategory() {return FruitCategory.SWEET;} },<br />
            ORANGE<br />
            { FruitCategory getCategory() {return FruitCategory.CITRUS;} },<br />
            GRAPEFRUIT<br />
            { FruitCategory getCategory() {return FruitCategory.CITRUS;} },<br />
            BANANA<br />
            { FruitCategory getCategory() {return FruitCategory.SWEET;} },<br />
            DURIAN<br />
            { FruitCategory getCategory() {return FruitCategory.SMELLY;} };
            <p>abstract FruitCategory getCategory();<br />
            }</p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
一旦创建一个类似这样的enum，就可以像对待其他对象那样来对待APPLE值(使用静态的Fruit.APPLE引用)，并且可以调用它的getCategory方法来得到它的相应类别。现在可以为上面的类添加一个main方法来说明如何使用新的Fruit enum：
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public static void main(String[] args) {<br />
            Fruit a = Fruit.APPLE;<br />
            // toString() returns "APPLE"<br />
            System.out.println ("The toString() for a: " + a);<br />
            // getCategory() returns "SWEET"<br />
            System.out.println ("a.getCategory() is: " + a.getCategory());<br />
            for (Fruit f : Fruit.values()) {<br />
            System.out.println ("Fruit is: " + f);<br />
            }<br />
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
正如代码中所示的那样，可以使用values方法来迭代enum内的所有值。enum的toString方法将会返回一个String，它具有和值相同的名字。使用enum而不是int来表示状态，可以使代码具有更好的可读性和更强的防错性。它明确地定义了一个特殊的枚举状态的所有值并可以防止有人使用不正确的值。</p>
 <img src ="http://www.blogjava.net/yydy1983/aggbug/221618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-08-13 10:06 <a href="http://www.blogjava.net/yydy1983/articles/221618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Wicked Cool Java中文版-1.1 在Java中没有for：使用增强的for循环</title><link>http://www.blogjava.net/yydy1983/articles/221616.html</link><dc:creator>♂游泳的鱼</dc:creator><author>♂游泳的鱼</author><pubDate>Wed, 13 Aug 2008 02:05:00 GMT</pubDate><guid>http://www.blogjava.net/yydy1983/articles/221616.html</guid><wfw:comment>http://www.blogjava.net/yydy1983/comments/221616.html</wfw:comment><comments>http://www.blogjava.net/yydy1983/articles/221616.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yydy1983/comments/commentRss/221616.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yydy1983/services/trackbacks/221616.html</trackback:ping><description><![CDATA[<p><strong>1.1&nbsp; 在Java中没有for：使用增强的for循环</strong>&nbsp; </p>
<p><font face="楷体_GB2312">Java5+</font></p>
<p>在一些编程语言中，通过列表或数组可以非常方便地进行遍历，通过一个循环即可逐个遍历项并将该项赋值一个局部变量从而实现自动循环。我曾经告诉过一个同事我认为Java中的&#8220;for&#8221;循环功能是不完全的，因为它没有&#8220;for-each&#8221;。我的朋友也是一个有经验的Java开发人员，他的回答是&#8220;你疯了吗？，在Java中当然有for！&#8221;在此之后很长一段时间里他为此而不断地嘲笑我，并定期地提醒我在Java中存在for(为了防止我遗忘此事) 。但是我有一个好消息要告诉他和所有Java开发人员：目前在Java中有了真正的for！</p>
<p>考虑这样一种情况，你希望对一个整型对象集合(如java.util.ArrayList)中的所有数值求和。你很可能编写过类似于下面这样的代码：</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>ArrayList theList = new ArrayList();<br />
            theList.add(new Integer(2));<br />
            theList.add(new Integer(3));<br />
            theList.add(new Integer(5));<br />
            theList.add(new Integer(7));<br />
            int sum = 0;<br />
            // The old way to iterate<br />
            for (Iterator iter = theList.iterator(); iter.hasNext(); ) {<br />
            Integer x = (Integer) iter.next();<br />
            sum = sum + x.intValue();<br />
            }<br />
            System.out.println("The sum is " + sum);  </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>这段代码多麻烦啊，难道编译器不应该知道你正在进行迭代吗?毕竟这是一个for循环，不是吗？而在Java 5中，增强的for循环现已支持集合对象。因此不再需要使用迭代器。在下面的修订的代码中，一个for循环通过列表进行迭代并显示出每个值：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>ArrayList&lt;Integer&gt; theList = new ArrayList&lt;Integer&gt;();<br />
            theList.add(2);<br />
            theList.add(3);<br />
            theList.add(5);<br />
            theList.add(7);<br />
            int sum = 0;<br />
            // new Java 5 iteration syntax<br />
            for (Integer item : theList) {<br />
            sum = sum + item;<br />
            }<br />
            System.out.println("The sum is " + sum); </pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>for循环定义了一个叫做item的局部变量，在每次迭代过程中，它将得到列表中的下一个值。除了完美的for循环语法外，此代码在以下两个方面也不同于过去的Java代码。</p>
<p><strong>使用了泛型</strong> </p>
<p>上面带有尖括号的语法是Java 5新增加的泛型特性。泛型允许为一些具体类型的对象定义类，但是直到创建该类的一个实例时才能知道具体的类型。编译器将会强迫执行类型检查。在这个示例中，ArrayList是一个特殊的类，对于add方法它只接受整数(并只从它的Iterator的next方法中返回整数)。这意味着当从列表中检索对象时不需要强制类型转换，可以立即将它们作为Integer实例来对待。不使用泛型时，仍可以使用新的for循环语法，但需要将Object强制转换成Integer。在1.4节中将我们更详细地介绍泛型。 </p>
<p><strong>整型对象到整型数值的自动转换</strong></p>
<p>在Java 5中，可以将Integer对象作为int来对待。编译器将自动执行从int到Integer对象的转换(反之亦然)，此过程称为自动装箱(autoboxing)。当循环中得到一个Integer对象时，可以将它与一个int值相加而不需要执行显式的转换。<br />
新的for语言也适用于数组：</p>
<p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>int[] theList = new int[] {2,3,5,7};<br />
            int sum = 0;<br />
            for (int x : theList) {<br />
            sum = sum + x;<br />
            }<br />
            System.out.println("The sum is " + sum);</pre>
            </td>
        </tr>
    </tbody>
</table>
这种新的语法的确使代码变得更加易读和紧凑。但还不能完全放弃迭代器，至少暂时是这样，因为还有很多开发人员没有将他们的JDK升级到版本5。<br />
</p>
 <img src ="http://www.blogjava.net/yydy1983/aggbug/221616.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yydy1983/" target="_blank">♂游泳的鱼</a> 2008-08-13 10:05 <a href="http://www.blogjava.net/yydy1983/articles/221616.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>