??xml version="1.0" encoding="utf-8" standalone="yes"?> Z查找单词Q需要捕获它前后的空?或标点符?。需要注意的是,如果此单词出现在data字符串的开_(d)那么上述的代码不?x)匹配它Q因为已假定它的前后存在I白W。除了单词repetition被替换外Q我们希望文本中的其他内容与原始文本保持一_(d)包括单词周围的空白字W。这里的元W号($)昄不表C币。它表示对从regex模式中捕L(fng)l?和组3的逆向引用Q包含最初匹配的I白或标点符受这P与它们对应的值将插入到替换文本中?/p>
Stringc?在JDK 1.4或更高的版本?有一个replaceAllҎ(gu)Q其工作机制cM于Matcher中的replaceAllҎ(gu)。这使得它可以很方便地替换一个与模式匚w的子Ԍ(x) replaceAllҎ(gu)q回一个新的字W串Q其中所有匹配的部分已被新值替换。但是,使用Matcher仍有很多优点Q因为相对于字符串它有更大的灉|性?/p>
可以使用Matcher的find循环用新gơ替换每个匹配的部分。这样你能够更好地控制替换q程。比如,可以在每ơ匹配的q程中应用其他逻辑Q甚xơ可以替换不同的倹{这qCStringBufferQStringBuffer用于保存更新的文本,q且一旦调用appendReplacementҎ(gu)QMatcherp加更新后的文本到~存中。在处理每个匚wq执行替换后Q需要用appendTailҎ(gu)最后一ơ匹配后的剩余字W串攄在输出缓存中。图2-1说明了子串匹配和q两U方法之间的关系?br />
在上一节中Q介l了用于匚w一个String中的模式和用于从一个子模式l中(g)索数据的正则表达式。用regexQ还可以用新的值替代匹配的模式。完成此操作的一U方法是使用MatchercȝreplaceAllҎ(gu)Q它?yu)返回一个字W串Q将所有匹配的子串替换为给定的字符丌Ӏؓ(f)了说明此Ҏ(gu)Q查找一个文件内出现的所有repetition单词q用单词duplication来替换它们:(x)
String data = getStringData();
Pattern repPattern = Pattern.compile("(\\s)(repetition)([\\s;\\.,])");
Matcher repMatcher = repPattern.matcher(data);
String newData = repMatcher.replaceAll("$1duplication$3");
String data = getStringData();
String result =
data.replaceAll("(\\s)(repetition)([\\s;\\.,])", "$1duplication$3");
Matcher有一个相应的append指针。指针最初从零开始,随着每次调用appendReplacement向前Ud。这U设计是Z在一个find循环内用它。每ơ匹配后Q调用appendReplacementҎ(gu)QMatcher指针所在的上一个位|到匚w之前指针所在的位置之间的内容,x更改的文本合q到StringBuffer中。然后,Matcher替换当前匚w的文本ƈ替换后的内Ҏ(gu)|在StringBuffer中。接下来QMatcherappend指针Ud到当前匹配结之后的W一个字W,然后重复此过E直C再生匹配。在扑ֈ所有匹配之后很可能剩下一个未匚w的部分。ؓ(f)了将q部分文本添加到输出StringBuffer中,使用appendTailҎ(gu)?br />
现在使用q些Ҏ(gu)前面的替换例子重写Z个@环。但是这一ơ对于每个匹配,用一个随机选择的同义词(repetition、duplication、copying、reiteration、recurrence或redundancy)来替代单词repetitionQ?
?-1 MatchercȝappendҎ(gu)
StringBuffer result = new StringBuffer();
String[] wordChoices = new String[]
{"repetition", "duplication", "copying",
"reiteration", "recurrence", "redundancy"};
Random rand = new Random();
String data = getStringData();
Pattern repPattern = Pattern.compile("(\\s)(repetition)([\\s;\\.,])");
Matcher repMatcher = repPattern.matcher(data);
while (repMatcher.find()) {
// pick a word at random
int wordIndex = rand.nextInt(wordChoices.length);
String replacement = "$1" + wordChoices[wordIndex] + "$3";
repMatcher.appendReplacement(result, replacement);
}
repMatcher.appendTail(result);
System.out.println(result);
可以按需求改写find循环中的逻辑来对每个匚wq行所需的处理。此外,q可以用前面讨的Matcher的方法:(x)group、start和end。可以用这些技术的l合有选择CҎ(gu)删除一个文件中每部分匹配的文本?
]]>
在上一节中Q介l了如何使用正则表达式在一个文件中q行搜烦(ch)以便(g)索它内部所有的URL。可以用Matchercȝfind、start和endҎ(gu)来检索匹配的URL字符丌Ӏ有时有必要q一步处理子串匹配的l果Q或是查N加的子模式。例如,Ҏ(gu)个特定区域的URL不进行处理。ؓ(f)了实现此目的Q一U强制性的Ҏ(gu)是用另一个Pattern和Matcher对象Q代码如下:(x)
// assume urlMatcher instance as in the previous example |
String urlPattern = |
String data = getStringData(); // load the document |
保存每个匚w的组以便可以随后引用它们。在一个模式内引用一个以前的匚wl称为逆向引用(backreference)。ؓ(f)了对W三个组q行逆向引用Q在模式中包括\3卛_。这会(x)只匹配一个与以前的组相匹配的严格重复的数据。ؓ(f)了说明此问题Q考虑一个在文本文g中常见的错误—?一个句子中意外地重复出现某个常用的单词Q如“the”?#8220;of”?/p>
" The the water molecules are made of of hydrogen and oxygen." |
下面~写一个模式来扑և文g中存在的q些问题。该模式捕L(fng)一个单词,后跟一些空白符Q而其后又跟着匚wW一个单词的重复模式Q?/p>
String wordPattern = "\\s(of|or|the|to)\\s+\\1[\\s\\.,;]"; |
String data = getStringData(); |
有一U简便和强大的匹配文件中文本的方法,该方法允怋用多个正则表辑ּ来处理文Ӟ本章后面?#8220;使用Scannerc进行语法分?#8221;一节将?x)讲解此?gu)。若想了解用内|烦(ch)引进行更为复杂的文本搜烦(ch)的解x法,请参考第3章中“使用Luceneq行搜烦(ch)”一节的内容?/p>
在Java中,通过使用适当命名的Patterncd以容易地定String是否匚w某种模式。模式可以像匚w某个特定的String值那L(fng)单,也可以很复杂Q需要采用分l和字符c,如空白、数字、字母或控制W。由于它们是Java字符串ƈ且基于Unicode(l一字符~码)Q正则表辑ּ也适用于国际化的应用程序?/p>
正则式是最单的能准匹配一个给定String的模式。换句话_(d)模式与所要匹配的文本是等L(fng)。静态的Pattern.matchesҎ(gu)用于比较一个String是否匚w一个给定模式?以下的代码将(g)查变量data中的值是否与单词“Java”相匹配:(x)
String data = getStringData(); // populate the String somehow |
对于直接匚w字符串和单的模式而言Q你很可能不?x)用正则表辑ּQ因实际上就是低效率版本?Java".equals(data)。Regex的真正强大之处体现在使用包括字符cd量词(*??)的更复杂的模式上。目前已有很多优U的有x则表辑ּ模式的书c,因此q里只讨论模式的一些基本特性ƈ重点讨论Java的regexcdҎ(gu)。ؓ(f)了快速了解这些特性,q里l出一些在正则表达式中使用的特D字W,其中每一个均代表一cdW,在regex术语中它UCؓ(f)字符c:(x)
\d 数字 |
大多数字W在一个模式表辑ּ中代表它们自己,但是一些字W有其特D含义。上面用的反斜?转义字符)是一个例子。下面的字符用于控制一个子模式应用到匹配过E的ơ数。这些特D字W的处理方式不同于其他字W:(x)
? 重复前面的子模式零次或一? |
下面的正则表辑ּ可以匚wM对先辈的U呼Q如father、great-great-grandmother或great-great-great-grandfather。正如下面的CZ所C,通过使用括弧中的子表辑ּ可以创徏更复杂的正则表达式:(x)
((great–)*grand)?(mother|father) |
接下来这个模式表辑ּ会(x)匚w一个以一个数字开头后跟零个或多个非空白字W的L字符?例如它将匚w“3”?#8220;5x”?#8220;56abcd9”Q而不匚w“8 5”?#8220;hello”)Q?/p>
\d\S* |
要}慎用regex反斜杠字W,因ؓ(f)在Java中它也是String文字转义字符。如果用一个String文字来保存正则表辑ּQ将需要通过使用两个反斜杠来转义反斜杠本w。例如:(x)
String digitNonSpacePattern = "\\d\\S*"; |
此外Q模式匹配也内置于StringcLw。在StringcM有一个新的简便方法:(x)matches。可以如下重写以上的代码Q?/p>
boolean isMatch = getStringData().matches("\\d\\S*"); |
Pattern.matchesҎ(gu)和String的matchesҎ(gu)都适合一ơ性用,但是若重复用它们的效率较低。通过使用静态的Pattern.compileҎ(gu)创徏一个Pattern实例可以得到一个更高效的用于执行多ơ匹配的~译版本的模式。Pattern对象可以和java.util.regex.Matchercd同工作。ؓ(f)了执行复杂的匚wQ需要创建Matcher(匚w?。Matcher一个模式表辑ּl定C个特定的字符Ԍ以便执行更高U的匚w操作。以下的代码D늼译了一个匹配仅由单个字W组成的模式Q?/p>
String data = getStringData(); |
要记住matchesҎ(gu)会(x)Ҏ(gu)个输入字W串和模式进行匹配。如果希望检查字W串是否仅以模式开_(d)则可以用lookingAtҎ(gu)Q?
boolean startsWith = nameMatcher.lookingAt(); |
在以下几个小节中会(x)讨论其他一些匹配技术,包括查找匚w一个模式的子串以及(qing)执行文本替换?
q里丑և一个Java~程E序员经常碰到的问题。例如现在是凌晨3点,在你喝完W?杯咖啡后Q你设法扑ֈ正确的逻辑来解军_杂的~程问题。到目前Q你几乎不能思考String和Object引用Q因Z已经昏昏Ʋ睡了。然后糟p的事情发生?#8230;…不,q不是Java溢出Q而是如下所C?/p>
String name = getName(); |
你快速编译ƈ试代码后,代码g正常q行。终于到下班回家休息的时候了Q然而,一D|间后Q应用程序测试发C一个间歇性错误,q跟t到此错误的来源恰好是这D代码?/p>
“怎么?x)这P”你可能会(x)愤怒地_(d)“前几天我q试验过cM的String比较Qƈ且能够正运行!”。但是,你需要首先重温一下Java对象引用的概c(din)一个对象变量是一个指向存储在堆内?heap memory)中实际对象的引用(指针)。当为另一个变量分配一个变量时Q事实上分配的是引用而不是实际的对象(如图1-1所C?Q?/p>
String a, b, c, d; |
![]() |
?1-1 对象引用 |
Java中,“==”q算W用来比较两个引用以查看它们是否指向同一个内存对象。而对于String实例Q运行时状态会(x)可能地保L两个h相同字符信息的String字面值指向同一个内部对象。此q程UCؓ(f)ȝ(interning)Q但是它q不有助于每个String的比较。一个原因是垃圾攉器线E删除了ȝ|另一个原因是String所在的位置可能被一个由String构造函数创建的新实例占用。如果是q样Q?#8220;==”Lq回false?br /> 可以设计equalsҎ(gu)来比较两个对象的状?state)或每个对象的内容。对你自qc,必须重写此方法来使它正确操作。但是如果用equalsҎ(gu)QString实例L能够正确地比较。假定所有的String值是ȝ的,下面的代码段说明了此问题Q?
String name1, name2, name3; |
注意Q?/strong>
L使用.equals来比较两个String|管使用“==”q算W看D够正操作。对于大多数应用E序而言Q即使它能正运行,?#8220;==”代码事实上是错误的,而只有equals是正的。因此告诉所有你的开发同行支持String?#8220;equals(q等)”权吧(q很可能是本书中最差的双关?Q?
Java1.1+
当进行Java开发时Q有旉要实C个仅包含1~2个方法的接口Qƈ且每个方法只?~2行代码。在AWT和Swing开发中l常?x)出现这U情况,例如当一个displaylg需要一个事件回调方?如一个按钮的ActionListener)时。如果用普通的cL实现此操作,最l会(x)得到很多仅在单个位置上用的型cR其实,Java允许定义内部c,而且可以在GUI架构外用内部类?/p>
内部c?inner class)是指在另一个类内部定义的一个类。可以将内部cd义ؓ(f)一个类的成员,如下例所C:(x)
public class Linker { |
q是一个在Java中实现的单链表。LinkedNodecLLinkercȝ内部cR尽此cd含在LinkercMQ仍可以从其他类讉K它,因ؓ(f)它定义为公有的(public)。通过使用new Linker.LinkedNode()Q可以从其他cd建它的一个实例。源自核心API的Map.Entrycd是这样一个类Q当在一个映表中检?#8220;??#8221;表项的集合时Qjava.util.MapcM(x)用到此类?/p>
此外Q还可以定义一个局限于一个方法的内部cR这U类型的cd在定义它的方法内是可见的。如果要实现一个接口ƈ且希望在Ҏ(gu)内不止一ơ地使用局部类Q那么很可能q样做,如下面这个实例所C:(x)
public class Happiness { |
Runnable runner = new Runnable(); // not allowed! |
Runnable runner = new Runnable() { |
Object timePrinter = new Object() { |
Java1.1+
正如上一节中所讨论的,Java 5在SystemcMd了一个nanoTimeҎ(gu)来确保时间度量能够适应更快的系l。即使在早期版本的Java中,U程可以h于1毫秒的休眠时间。回想一下Java的线E机制有一个sleepҎ(gu)Q该Ҏ(gu)采用一个int参数来表CZ毫秒计量的休眠时间。此外还有一个sleepҎ(gu)Q它接受一个毫U参数和一个以U秒计量的时间参数。如果将毫秒旉设ؓ(f)Ӟ那么U程会(x)休眠指定的纳U?ns)数?/p>
public class MyThread extends Thread { |
Java5+
摩尔定律是一U众所周知的现象,卌机中的晶体数量和它的处理速度随时间呈指数规律增长。作Z童半g公司(Fairchild Semiconductor)的研发领ghQ戈?#8226;摩尔?965q提Zq一伟大发现。迄今ؓ(f)止,它仍有效?br /> 与Java首次出现的时候相比,当前计算机的速度要快得多Q对于很多应用程序而言以毫U计时已不再能够满要求。你可能使用qjava.lang.Systemc,利用currentTimeMillisҎ(gu)来获得一个方法调用或一D代码的定时信息。此Ҏ(gu)可以用来度量执行某操作所p的时间。但是,在运速度更快的计机上操作花费的旉可能q小?毫秒Q于是可以在一个for循环中执行此操作上百ơ或上千ơ,然后除以循环ơ数来计此操作的单位时间。考虑下面的示例:(x)
long startTime = System.currentTimeMillis(); |
q种一U很单的q算Q因Z用了for循环1000ơ。但是如果要度量亚微U该如何实现呢?
for(int i=0; i<1000000; i++) { performOperation(); } |
List myList = initializeList(); // initialize the List somehow |
遗憾的是Q运行上面的代码时无法保证实际上获得的是U秒U的度量。但是用更快的机器和良好的JRE实现Q对于测试目的而言它是一U有用的度量Ҏ(gu)。可以在JDK 5文档中找到更多有xҎ(gu)的信息。鉴于操作系l特性、机器处理速度和系l负载的不同Q得到的由nanoTimeҎ(gu)q回的值可能会(x)有很大的变化。随着旉的推UL问题应该?x)有所改善Q摩?dng)定律基本上能保证这一炏V?/p>
参考资料:(x)
惌了解摩尔的原始论文,请参看Gordon E. Moore, Cramming More Components onto Integrated Circuits, Electronics, Vol. 38, No. 8 (April 19, 1965)。此外,q可以在|上获得该论文,参看本书的网站http:// wickedcooljava.com以获得URL?/p>
Java5+
“~程人员L正确的—?是编译器和解释器造成的错误?#8221;我确信你认同q种说法。作为编Eh员,l常要对变量的值做出假讑ƈ且基于此~写代码。尽非怸愿意承认可能在设计或实现上有错误Q但有时变量和参数却没有获得期望的倹{?/p>
当设计和~写代码Ӟ只有在最初的假设仍然成立的情况下代码才能正确q行。如果没有Q何有兌些假讄声明Q那么阅M码的M?甚至你自?都不清楚它们的含义是什么。从而导致今后的改动可能?x)违反这些假讑ƈ引入难以查找的错误。通常Q在注释中说明假设能使以后修改代码的人避免出错?/p>
使用注释来说明假设是一个好的开始。但是当出现q反假设的情冉|Q程序有时会(x)l箋q行像未出CQ何问题一栗一些情况下Q开发h员能够马上看到结果,q且可以U正出现的问题。但在另一些情况下Q存在一个潜伏的错误Ӟ可能?x)对应用E序的其他部分造成负面影响Q对于分布式pȝ而言Q则可能?x)对完全不同的另一个应用程序造成负面影响Q跟t这L(fng)问题非常困难?/p>
Java 1.4在语a中添加了断言Ҏ(gu)来化测试和调试Q加强文档编制ƈ提高ZJava的可l护性。可以用一个布?yu)(dng)表辑ּ来创Z个断aQ以便测试有关系l的当前状态所假定的某些情c(din)如果断ap|Q运行库?x)抛Z个AssertionError。下面给Z个很单的断言Q?/p>
String name = "Brian"; |
q里Q可以确定在lname分配了?#8220;Brian”后它的值将不会(x)为空。如果它的gؓ(f)I,则出C某种严重的错误!此断a是当时在E序中对变量的值所做的假定的声明。ؓ(f)如此单的例子做这U声明看似可W和多余。但是,当多个方法会(x)影响一个对象的状态时Q这U方法是有效的。在下面的示例中Q示例了q样一个断aQ即在向新员工分配Q何Q务之前必dl指z了一名管理h员?/p>
Employee worker = |
通常Q在Ҏ(gu)个对象执行关键操作时?x)需要对它创建断a。这有助于增Z码的健壮性,比如如果在程序中出现了某U错误,可以更方便地调试E序。这样做要比E序在某处执行失败造成不良后果来发现错误要好得多。当知道E序p|是由于它q反了假设而引L(fng)时候,跟踪p|的原因要单得多。在q个代码样例中,使用了assert选项来返回更有用的信息。没有此选项Ӟ除了行号之外无法得到有xa的标识信息?
在某些版本的~译器上Q当~译源代码时需要用一个命令选项来设|编译器的源兼容性模?取决于编译器的版本,?.4?.5)?/p>
javac -source 1.5 MyClass.java |
现在强制断言p|q观察会(x)出现什么情况:(x)
public class AssertBad { |
默认情况下,q行时环境不支持断言Q必M用ea(允许断言)命o(h)选项来启动JRE。上面的代码?x)引起以下的l果Q?/p>
C:\projects\wcj1> java -ea AssertBad |
要记住断a是用来对那些不应该出现的情况q行实际?#8220;健全性检?#8221;Q因此不应该使用它们来替代常规的错误(g)查?/p>
警告Q?/strong>
不要让断a语句更改代码中的状?倹{否则当最l关闭断aӞ代码的行为方式将不同于启用断a时代码的行ؓ(f)。例如,不要创徏如下的断aQ?/p>
assert (++i > 10); // BAD: i changes only with assertions enabled!
通常Q在整个开发阶D都?x)启用断a。一旦完全测试了pȝq将它移送到产品环境Ӟ则希望禁用断aQ因样做?x)略微改善性能。但是不要改动代码来完成此操作,q且也不要删除断a。不怎样Qؓ(f)了编制文档的目的Q断a也应保留在代码中。这P当以后更改代码时Q会(x)提醒E序员要保持所有假N是有效的Qƈ且这也是可测试的?br />
Java5+
Java~程人员常常需要ؓ(f)Ҏ(gu)定义一个包含多个值的参数。这时可以采用List或数l的形式Q如下例所C?br />
public int add(int[] list) { |
也可以将它实Cؓ(f)几个重蝲的方法,每个Ҏ(gu)接受不同数量的int参数。这样做有时可以令方法更Ҏ(gu)使用Q因用代码不需要首先创Z个数l?/p>
public int add(int a, int b) { |
如果只有量|q对于调用代码更为方便,因ؓ(f)现在可以使用add(12,14,16)来代替add(new int[] {12,14,16})。但是,~写cMq样的方法存在问题,需要ؓ(f)每种可能的参数组合编写不同版本的Ҏ(gu)。如果希望在Ҏ(gu)的用方式上h最大的灉|性则与其创徏一个庞大的h上千个方法的c,不如寚w载的Ҏ(gu)接受的参数数量进行很的限制?/p>
在Java 5中,可以~写一个方法以使它允许可变数量的参数ƈ让编译器完成列表包装到一个数l中的操作。虽然内部仍是处理数l,但此时的~译器已隐藏了细节。以下的代码使用可变参数(vararg)重写了addҎ(gu)?
public int add(int... list) { |
注意Q那些奇怪的圆点正是可变参数的实际语法!q且Q由于这U改动要用到Java 5Q我们也可以机使用Java 5增强的for循环语法。一旦按照这U方式编写了Ҏ(gu)Q可以用实际数量的参数来调用它Q此外,q可以传递一个作为参数的数组(但不允许是List或Collection对象)Q?
add(1,3,5,7,9,11,13,15,17,19,21,23,25); |
public void badMethod(int... data, String comment) { } // wrong! |
public void goodMethod(String comment, int... data) { } |
Java5+
前面的小节介l了泛型可以化Java代码q代码能够防范ClassCastException错误。除了作为JDK的一部分来用泛型之外,q可以编写你自己的泛型。当对类型相同的对象q行操作时泛型是很有用的Q但是对象的具体cd直到对类实例化时才能知道。这U方式非帔R合于包含关联项目的集合或涉?qing)查扄cR?/p>
下面~写一个用泛型参数的Ҏ(gu)。回想一下前面是怎样使用ArrayListcȝ—?只在构造ArrayList时才指定它用哪些对象类型。注意,在定义类时ƈ不知道其cdQƈ且不能将java.lang.Object作ؓ(f)cd使用Q因为最后将遇到cM以前的类型强制{换问题。当定义泛型Ӟ必须使用一U特D的语言来代表类型。当声明cd时要完成此操作。在下面的示例中Q?lt;T>表示一U类用的cdQ?/p>
public class RandomSelection<T> { } |
q里Q类型指C符的尖括号看v来类似HTML语法Q但实际上和HTML没有关系Q它们也不表C小于或大于Q尖括号在一个泛型的cd与一个类型相l合的情况下使用Q正如前面的ArrayList<Integer>那样。尽直到调用构造函数时才知道真实的cdQ但我们可以在方法定义中使用替换cd。假定定义了一个叫做RandomSelection的类Q该cM用另一个类的某个类型,暂时该cdUCؓ(f)T。但是,此类的名字仍是RandomSelection。另外,每次可以对多个类型执行这U操作,正如java.util.Map的定义所C的那样。在q种情况下,在类名之后用一个由逗号分隔的标识符列表卛_?
public class MyGeneric<T,U,V> { } |
上面定义的MyGenericcL?qing)到三个cdQ它们分别称为T、U和V。下面编写一个方法来扩展RandomSelectionc,该方法将一个项目添加到一个由内部理的泛?cd为T)的ArrayList中:(x)
public class RandomSelection<T> { |
注意Q实际上q不是处理一个叫作T的类。T代表当某人创建RandomSelection的一个实例时使用的Q意类。Java规范允许使用L标识W,但是一般是使用单个大写字母来和普通的cdq行区别。既然已l定义addҎ(gu)接受一个类型T参数Q则只能使用在构造RandomSelection实例旉用的相同的类型来调用此方法。以下的代码是非法的q会(x)产生一个编译错误:(x)
RandomSelection<String> rs = new RandomSelection<String>(); |
import java.util.Random; |
RandomSelection<Integer> selector = new RandomSelection<Integer>(); |
RandomSelection<Fruit> fruitSelector = new RandomSelection<Fruit>(); |
Java5+
正如前面讨论for循环时看到的那样Q用泛型有助于化代码ƈ降低出错概率。for循环?x)假定ArrayList仅包含I(xin)nteger对象Q因为ArrayList严格地被定义为由Integer对象l成。因而当从列表检索项目时可以避免从Object到Integer的强制类型{换?/p>
Java 5Ҏ(gu)心API做出了很多利用泛型的更改。查看相x档你?x)发现已重新定义了很多类以允怋用泛型。如果愿意的话,仍可以按照以前的方式构造和使用q些c,例如使用new ArrayList()。这样做的原因是Z兼容性,以便仍可以在旧版本的~译器下q行代码。当?dng)q样?x)失L型提供的cd(g)查带来的便利性和安全性?/p>
一个得到很好修订的cLjava.util.Map (和HashMap)。我们知道,映射操作像查表一P每个值都存储在一个唯一的键标下。在早期的Java版本中,当在映射表中攄表项Ӟ它们是作为Object存攄。当从映表中检索表Ҏ(gu)Q它被作为标准的Object引用来对待,卌强制转换成正的子类以便能够识别为它的实际类型。这与List中存在的危险相同。要么对象不正确Q要么出现ClassCastException异常Q这L(fng)情况太常见了?/p>
假定有一个用于维护员工数据的EmployeecR下面给Z些用HashMap的典型代码:(x)
Employee brian = new Employee(); |
在检索项目时Q最大的危险位于强制cd转换的过E中。用Java 5Q不用强制类型{换也可完成此操作Q只要用正的cd来实例化Map。可以对键和值的cddU束。在下面的示例中Q只允许String键和Employee|(x)
Employee brian = new Employee(); |
Java5+
大多数应用程序需要记录一个值的有限集—?卛_用程序中表示一l选择或状态的帔R。一U常见的Java~程惯例是用static int变量来表C些倹{然后让E序通过比较q些值和其他变量的值来做出军_。尽核心Java API本n也采用这U惯例,但它很可能导致严重的问题Q下面是一个有x果信息的CZ。该CZl出了用int变量来表C枚举数据时?x)出现的一些问题?
public class FruitConstants { |
enum Fruit {APPLE, ORANGE, GRAPEFRUIT, BANANA, DURIAN} |
public enum Fruit { |
Fruit.getCategory(Dessert.PIE); // compile error |
public enum Fruit { |
public static void main(String[] args) { |
Java5+
在一些编E语a中,通过列表或数l可以非常方便地q行遍历Q通过一个@环即可逐个遍历ƈ该赋g个局部变量从而实现自动@环。我曄告诉q一个同事我认ؓ(f)Java中的“for”循环功能是不完全的,因ؓ(f)它没?#8220;for-each”。我的朋友也是一个有l验的Java开发h员,他的回答?#8220;你疯了吗Q,在Java中当然有forQ?#8221;在此之后很长一D|间里他ؓ(f)此而不断地嘲笑我,q定期地提醒我在Java中存在for(Z防止我遗忘此? 。但是我有一个好消息要告诉他和所有Java开发h员:(x)目前在Java中有了真正的forQ?/p>
考虑q样一U情况,你希望对一个整型对象集?如java.util.ArrayList)中的所有数值求和。你很可能编写过cM于下面这L(fng)代码Q?/p>
ArrayList theList = new ArrayList(); |
q段代码多麻?ch)啊Q难道编译器不应该知道你正在q行q代?毕竟q是一个for循环Q不是吗Q而在Java 5中,增强的for循环现已支持集合对象。因此不再需要用P代器。在下面的修订的代码中,一个for循环通过列表q行q代q显C出每个|(x)
ArrayList<Integer> theList = new ArrayList<Integer>(); |
for循环定义了一个叫做item的局部变量,在每ơP代过E中Q它?yu)得到列表中的下一个倹{除了完的for循环语法外,此代码在以下两个斚w也不同于q去的Java代码?/p>
使用了泛?/strong>
上面带有括L(fng)语法是Java 5新增加的泛型Ҏ(gu)。泛型允ؓ(f)一些具体类型的对象定义c,但是直到创徏该类的一个实例时才能知道具体的类型。编译器会(x)执行cd(g)查。在q个CZ中,ArrayList是一个特D的c,对于addҎ(gu)它只接受整数(q只从它的Iterator的nextҎ(gu)中返回整?。这意味着当从列表中检索对象时不需要强制类型{换,可以立即它们作为Integer实例来对待。不使用泛型Ӟ仍可以用新的for循环语法Q但需要将Object强制转换成Integer。在1.4节中我们更详细Cl泛型?
整型对象到整型数值的自动转换
在Java 5中,可以Integer对象作ؓ(f)int来对待。编译器自动执行从int到Integer对象的{?反之亦然)Q此q程UCؓ(f)自动装箱(autoboxing)。当循环中得C个Integer对象Ӟ可以它与一个int值相加而不需要执行显式的转换?br /> 新的for语言也适用于数l:(x)
int[] theList = new int[] {2,3,5,7}; |