Jakarta Commons Cookbook 中文版


11 第 1 章 扩展 J2SE 平台 1.0 简介 本章将介绍几种扩展Java 2标准版( J2SE)功能的实用程序,它们提供的类能让某些枯 燥的编程工作更加直观易行。Jakarta Commons Lang针对J2SE集成了许多有益的补充。 它填补了标准 JAVA API 未能涉及的部分,提供了很多简单易用的工具类。Sun 公司 Javadoc指出,J2SE 中的java.lang包“提供了Java语言程序设计所必需的基本类”。 类似地,Jakarta Commons Lang 提供了对 Java 语言基本设计的增强。 你可能跃跃欲试想跳过本章简单的内容,直接转到书中更复杂的部分。的确,字符串操 作、日期截取和toString()方法不会让程序员体验到在处理可扩展标记语言(Extensible Markup Language,XML)或者文本语音转换引擎时油然而生的好奇心和成就感。不过, 即便最熟练的Java程序员也能从本章学到很多东西。学习一些简单的技巧,可以让你每 天节省好几分钟时间。别把时间浪费在重写和维护Commons Lang包里早已存在的东西 上,还有更多更有趣的问题等着你解决呢,而编写 hashcode()函数显然不在其列。 Tiger 的出现 随着“Tiger”(Java1.5 的代号)的发布,上述实用程序有的被复制,有的则被替 代。那些还需继续使用 1.4 版的用户会发现本章很有帮助,你不必担心新的 Java Virtual Machine(JVM)是否可用。即使你在使用 1.5 版,本章的很多内容仍然颇 有用处。随着 java 1.4 的发布,Jakarta Commons项目中的一些类已经过时。不过 还有人在使用 Java1.3(甚至 1.2)运行程序。总有一天实现向后兼容的障碍会被解 决,不过当前大部分 Commons 组件都要求至少运行在 1.3 或 1.4 版上。 第 1 章12 1.1 获取 Commons Lang 问题 需要获取Commons Lang组件,以便使用它提供的某些简单易用的实用程序,譬如数组 操作、日期操作以及枚举等。 解决方案 要下载 Commons Lang 的最新版本,请遵循如下步骤: 1. 在 Web浏览器中,输入 URL:http://jakarta.apache.org/site/binindex.cgi。此 URL 将选定一个镜像站点,生成下载页面,通过页面上的链接可以从 Apache镜像站点 下载二进制发布版。 2. 找到Commons Lang项目。若搜索“Commons Lang”,你将找到用以下载Commons Lang 最新二进制发布版的链接。 3. 点击 2.0 zip 或 2.0 tar.gz(由系统平台而定),下载 Commons Lang 2.0。 4. 使用 Unzip或 untar 命令解压该二进制发布文件,得到 commons-lang-2.0目录。该 目录中包含一个 Java 存档文件(Java Archive,JAR)commons-lang-2.0.jar。 5. 复制或添加 commons-lang-2.0.jar 文件到类路径(class path)。 讨论 上述第 5 步因开发环境而异。若以 Apache Ant 作为构建工具,你需要确保类路径中包 含 Commons Lang JAR 包。如果使用像 Eclipse 一样的集成开发环境(IDE),你需要将 上述 JAR 文件复制到项目目录里,改变项目参数,添加 Commons Lang 2.0 至“Java构 建路径”中。如果以 Maven 作为构建工具,你需要在 Project.xml 的 dependencies 段中 添加如下依赖关系: commons-lang 2.0 ...other dependencies... Commons Lang库包含了处理日期、异常、数组、枚举等内容的实用程序。它是 Java开 13扩展 J2SE 平台 放源代码使用最广泛的库。欲学习更多 Commons Lang 的内容,可访问其 Web 站点 http://jakarta.apache.org/commons/lang/。 注意:在本书编写时,Commons Lang 的最新版本是 2.0 版。如果现在出现了更新的版本,请使 用新版本。虽然无法避免万一的情况, 大部分Jakarta Commons组件都竭力保证向后兼容。 所有接口的改变或者功能的去除都会在新版本的发布记录中予以说明。如果你准备按照本 书的指导使用其他版本 Commons Lang 组件,请务必阅读 RELEASE-NOTES.txt 文件中的 发布记录。 参考 如果你想就 Commons Lang 的某个实用程序提问,请参考 1.2 节加入 Commons-User 邮 件列表。如果你想得到源代码,请参考 1.3 节获取 Commons Lang 的源代码。此外,你 可以访问 Commons Lang 的 Web 站点:http://jakarta.apache.org/commons/lang。 1.2 加入 Commons-User 邮件列表 问题 需要就 Jakarta Commons 中某个组件提问题。 解决方案 加入邮件列表commons-user@jakarta.apache.org,你可以在上面提问。该邮件列表的成 员们都是 Commons 的使用者,不论你在 Jakarta Commons组件的哪个实用程序上碰到 问题,都可以在这里提出来讨论。 讨论 在加入此邮件列表之前,不妨抽出几分钟时间阅读一下它的使用指南: http://jakarta. apache.org/site/mail.html。在 http://jakarta.apache.org/site/mail2.html#Commons 上也 能了解到如何加入本邮件列表。 在 Jakarta Commons 邮件列表中,任何 Commons 组件用户都可以向其他用户或者 Commons开发者提问。该邮件列表含有海量的消息,订阅者应在邮件标题前加上讨论的 组件名称,以免混淆。譬如就 Commons Lang 而言,应确保发送给 Commons-User 邮 件列表的邮件标题以[lang]开头,否则你的邮件将被忽略。 第 1 章14 假如有问题或评论,不妨搜索一下 Commons-User 邮件列表的存档( http://www.mail- archive.com/commons-user@jakarta.apache.org/),这样能节省不少时间,少走很多弯 路。如果对某个Commons组件有问题,那么查找该邮件列表存档是一个很好的办法。不 要问已经解决的问题,此外请记住, Apache Software Foundation(ASF)是一个志愿 者组织,如果提供了足够详细的信息,每个人都很乐意帮助你。如果你能解答其他人的 问题,也千万别害羞,你不必征得任何人的同意,积极参与进来分享经验就好。 参考 Commons-User邮件列表存档也可从Eyebrowse列表存档中获得:http://nagoya.apache. org/eyebrowse/SummarizeList?listId=15。 1.3 获取 Commons Lang 源代码 问题 希望阅读 Commons Lang 项目的源代码。 解决方案 访问 http://jakarta.apache.org/site/sourceindex.cgi,下载源代码。遵照与 1.1 节相同的 步骤,下载到名为 commons-lang-2.0-src.zip(或者 commons-lang-2.0-src.tar.gz)的文 件。解压该文件,Commons Lang 的源码就位于 ./commons-lang-2.0-src 目录下。 讨论 上述 commons-lang-2.0-src 目录包含以下内容: build.xml Apache Ant构建文件,用于编译源代码。在安装好Ant后,可分别运行ant compile 或 ant test 命令编译、测试源代码。 src/java 该子目录包含 Commons Lang 各个类的源代码。 src/test 该子目录包含Commons Lang项目单元测试代码。其中每个 *Test.java文件都继承 了 Junit TestCase 类。 15扩展 J2SE 平台 参考 欲获得更多有关 Apache Ant 的信息,可访问其项目主页 http://ant.apache.org。也可参 考 Eric M. Burke 和 Brian M. Coyner 编写的 Java Extreme Programming Cookbook (O'Reilly 出版)书中第 3 章。 该源代码发布版中的单元测试代码使用JUnit测试框架实现。有关JUnit的更多信息请参 考其 JUnit 项目主页:http://www.junit.org。此外也可参考 Burke 与 Coyner编写的 Java Extreme Programming Cookbook 一书第 4 章。 欲了解如何获得 Commons Lang 二进制发布版,请参考 1.1 节,或参考 Commons Lang 的 Web 站点:http://jakarta.apache.org/commons/lang。 1.4 自动生成 toString()内容 问题 希望能自动生成 toString()方法 解决方案 使用 Commons Lang 的 ReflectionToStringBuilder 或 ToStringBuilder,配合 ToStringBuilder 可生成 toString()方法。下面的代码显示了如何使用反射生成器 (reflection builder)生成 toString()方法: import org.apache.commons.lang.builder.ReflectionToStringBuilder; public String toString() { return ReflectionToStringBuilder.toString(this); } 讨论 假设类PoliticalCandidate是一个bean,用以存储总统候选人的部分信息。该 Bean含 有一系列属性,包括 fristName、lastName、dateOfBirth、moneyRaised 和 homState。例 1-1 中显示了一个使用 ReflectionToStringBuilder 的 PoliticalCandidate 类。简洁起见,程序里的 getter 和 setter 方法已被省略。 例 1-1:使用 ReflectionToStringBuilder 的 PoliticalCandidate 类 import java.math.*; import java.util.*; 第 1 章16 import org.apache.commons.lang.builder.ReflectionToStringBuilder; public class PoliticalCandidate { private String lastName; private String firstName; private Date dateOfBirth; private BigDecimal moneyRaised; private State homeState; // get/set methods are omitted for brevity... public String toString() { return ReflectionToStringBuilder.toString(this); } } 让 toString()的内容与不断变化的类保持一致,是一项让人心烦又容易遗忘的工作。 Commons Lang带有一个非常易用的工具类,通过反射( reflection)自动完成这件麻烦 事。ToStringBuilder 类及其派生类 ReflectionToStringBuilder 能把原本臃肿的 toString()方法浓缩成一行。更重要的是ReflectionToStringBuilder反映了对象模 型未来的变化趋势。下列代码将显示通过反射得到的字符串。 // Create a State State va = new State( "VA", "Virginia"); // Create a Birth Date Calendar calendar = new GregorianCalendar(); calendar.set( Calendar.YEAR, 1743 ); calendar.set( Calendar.MONTH, Calendar.APRIL ); calendar.set( Calendar.DAY_OF_MONTH, 13 ); Date dob = calendar.getTime(); BigDecimal moneyRaised = new BigDecimal( 293829292.93 ); // Create a Political Candidate PoliticalCandidate candidate = new PoliticalCandidate( "Jefferson", "Thomas", dob, moneyRaised, va ); System.out.println( candidate ); 假定 State对象是另一个使用 RefalectionToStringBuilder 的 bean,上述代码将设 置 bean 的属性,并产生如下输出: com.discursive.jccook.lang.builders.PoliticalCandidate@187aeca [lastName=Jefferson,\firstName=Thomas, dateOfBirth=Sat Apr 13 22:38:42 CST 1743, moneyRaised=\293829292.930000007152557373046875, state=\com.discursive.jccook.lang.builders.State@87816d [abbreviation=VA,name=Virginia]] 17扩展 J2SE 平台 注意:与本书其他示例一样,上面的示例对输出格式做了调整以便印刷。你得到的结果在内容上 和本例所示完全相同,不过在格式上将是相当长的一行。 这些信息看上去有点别扭,可它们是自动生成的。在有限的时间和预算条件下,面对含 有上百个实体的对象模型,要保证 toString()方法能实时更新几乎就是天方夜谭。如 果你的类拥有含义明确的 toString()方法,在诊断程序时将会受益匪浅。通过使用 ReflectionToStringBuilder 类,能确保输出是正确的。依靠开发者手动维护 toString()方法会很不可靠,现在已不再流行。 警告:本工具类使用J2SE反射(reflection)包中的AccessibleObject类绕过访问控制而直接存 取对象的私有成员。如果你的系统运行在受限的安全管理器下,可能需要改变配置,以便 让Commons Lang绕开其安全限制。只有在确定你的代码将运行于不受限的安全策略下时, 才可以使用反射生成器。如果是为情况明确的系统开发软件,你可以使用这个工具类;不 过,若是编写一个复用的库,反射生成器则可能出问题。一旦别人在不同的安全策略下使 用你的库,在调用 toString()时就可能出错。上述情况的相关权限为 java.lang. reflect.ReflectPermission 的suppressAccessChecks。 1.5 自定义 toString()内容 问题 在维持能控制其输出、内容和格式时才需要自动生成 toString()。 解决方案 在提供ReflectionToStringBuilder的同时,Commons Lang也通过ToStringBuilder 和 ToStringStyle 类提供了定制功能。如上一章所示,如果仅需要 Political- Candidate 的 toString()方法输出 lastName 和 firstName,可以通过传给 ToStringBuilder 类一个 ToStringStyle 对象来使用它。下面的示例实现了一个既定 制了样式又定制了内容的 toString()方法: import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append("lastName", lastName) .append("firstName", firstName) .toString(); } 第 1 章18 上述 toString()方法只输出由 append()参数指定的那两个属性,如下所示: com.discursive.jccook.lang.builders.PoliticalCandidate@1cd2e5f[ lastName=Jefferson firstName=Thomas ] 注意:与1.4 节不同,此处的输出和在计算机屏幕上的实际输出完全一样。 讨论 使用反射生成 toString()的内容有利于节省时间,但这样做只能全部输出该类的成 员变量。如果想输出指定的几个变量怎么办?如果类里面的一些内容不便输出呢?此外, 一个对象可能有很多属性,或者某个属性含有大量的文本内容,在这种情况下,使用 ReflectionToStringBuilder 输出类中每个变量是不可行的。 使用ToStringBuilder时,可借助ToStringStyle类的静态成员来定制输出内容。 ToStringBuilder的构造函数接受一个对象实例和一个ToStringStyle样式作为 参数,返回一个 ToStringBuilder 类实例。该生成器接着通过 append()被定制, append()方法指定了哪些属性会被输出。定制 ToStringBuilder 时,你需要手动 添加各个属性到生成器实例中。Append()方法可以接受所有基本类型,对象和数组。 下面的表 1-1 总结了 append()方法使用整型和对象时的各种形式。 表 1-1:ToStringBuilder append()的各种形式 ToStringBuilder 方法 描述 append(int value) 添加整数值 append(String n,int value) 添加整数值和属性名 append(Object value) 添加对象的 toString()方法 append(String n,Object value) 添加对象的toString()方法和属性名 append(int[] array) 添加格式化后的数组内容 append(Object[] array) append(String n, int[] array) 添加属性名和数组大小 append(String n, Object[] array) append(String n,int[] array, boolean detail) 添加属性名和数组全部内容 ToStringStyle类提供了对ToStringBuilder类输出进行定制的机制,同时也提供几种 内建样式。ToStringStyle.MULTI_LINE_STYLE是这些样式之一,其作用是在每两个属 19扩展 J2SE 平台 性之间插入换行符。另一个例子是 ToStringStyle.SIMPLE_STYLE,它的作用是简单地 输出每个变量的值。下表中为每个内置样式都提供了一个示例: ToStringStyle.DEFAULT_STYLE com.discursive.jccook.lang.builders. PoliticalCandidate@1cd2e5f[lastName=Jefferson,firstName=Thomas] ToStringStyle.MULTI_LINE_STYLE com.discursive.jccook.lang.builders.PoliticalCandidate@1cd2e5f[ lastName=Jefferson firstName=Thomas ] ToStringStyle.NO_FIELD_NAMES_STYLE com.discursive.jccook.lang.builders.PoliticalCandidate@1cd2e5f[Jefferson,Thomas] ToStringStyle.SIMPLE_STYLE Jefferson,Thomas 对 toString()方法而言最重要的是什么?一是准确,二是要紧紧跟上不断变化的系统 内容。任何实用的错误信息里都带有显示出错对象内容的文本信息。出现异常时,只要 打印出对象的值,往往就能很容易知道是什么引起了异常。在缺乏日志系统的情况下, 离开调试器就几乎无法调试程序,尤其是在诊断一个只影响很少人的罕见问题,而这个 问题只有在特定场景下执行特定的操作才会碰到。这时,最好能知道和错误相关的每个 对象的内部状态,而 toString()方法是显示这些信息的最简便办法。自动生成 toString()方法会使你的错误提示信息更有意义。 1.6 自动生成 hashCode()和 equals() 问题 你需要一种自动生成 equals()和 hashCode()的方法。 解决方案 Commons Lang 包中的 EqualsBuilder 和 HashCodeBuilder 类提供了自动生成 equals()和hashCode()方法的功能。例1-2 通过与上两节相同的PoliticalCandidate bean 示范了这两个生成器的使用方法。 例 1-2:自动生成 equals()和 hashCode() import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.EqualsBuilder; 第 1 章20 public class PoliticalCandidate { // Member variables - omitted for brevity // Constructors - omitted for brevity // get/set methods - omitted for brevity // A hashCode which creates a hash from the two unique identifiers public int hashCode() { return new HashCodeBuilder(17, 37) .append(firstName) .append(lastName).toHashCode(); } // An equals which compares two unique identifiers public boolean equals(Object o) { boolean equals = false; if ( o != null && PoliticalCandidate.class.isAssignableFrom(o.getClass() ) { PoliticalCandidate ps = (PoliticalCandidate) o; equals = (new EqualsBuilder() .append(firstName, ps.firstName) .append(lastName, ps.lastName)).isEquals(); } return equals; } } 讨论 HashCodeBuilder 的构造函数使用两个整数作为输入。这两个整数在生成散列码 (HashCode)时充当偏移值,它们必须都是非零非偶的质数。例1-2中HashCodeBuilder 被设置为使用 PoliticalCandidate 对象的 firstName 和lastName 值,故而两个拥有 相同 firstName 和 lastName 值的 PoliticalCandidate 对象将生成同样的散列码。如 果一个散列码与类中所有域都有关,那就可以通过反射( reflection)来生成它: public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } 与 ToStringBuilder以及HashCodeBuilder类似,EqualsBuilder类也通过append() 方法进行配置,append()接收两个参数以作比较。EqualsBuilder类的append()方 法可以接受所有基本类型、对象以及数组作参数。通过传递两个数组到其 append()方 法,EqualsBuilder类能够方便地比较它们。此时,这两个数组的所有元素都将被逐个 比较: int[] array1 = new int[] { 1, 3, 4, 2, 5, 3, 4, 5, 3, 4 }; int[] array2 = new int[] { 1, 3, 4, 2, 5, 3, 4, 5, 3, 4 }; int[] array3 = new int[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; EqualsBuilder builder = new EqualsBuilder(); 21扩展 J2SE 平台 builder.append( array1, array2 ); boolean firstTwoEqual = builder.isEquals(); System.out.println( "Is array1 equal to array2? " + firstTwoEqual ); EqualsBuilder builder = new EqualsBuilder(); builder.append( array2, array3 ); boolean lastTwoEqual = builder.isEquals(); System.out.println( "Is array2 equal to array3? " + lastTwoEqual ); EqualsBuilder 将比较两个数组的内容,检查每个对应元素是否相等。其输出如下: Is array1 equal to array2? true Is array2 equal to array3? False 如果两个类相等等价于其所有对应域都相等,那么EqualsBuilder可以通过反射比较两 个对象的大小。代码如下: public boolean equals(Object o) { return EqualsBuilder.reflectionEquals(this, o); } 警告:在使用反射( reflection)自动生成hashCode()和equals()时请务必小心,你将可能得到 意想不到的结果。在例 1-2 中,candiadate由其 firstName与lastName 唯一确定,如果这 个 bean被映射到关系数据库的一个表中,则 firstName 与lastName将成为区分不同行的 复合主键。HashMap或者HashSet与数据库的表相类似,它们使用hashCode()和equals() 区分不同对象。如果含有相同的散列码,则后来者将替换前面的项。如果类的 equals() 和 hashCode()方法实现不佳,则在存储该数据结构的对象时将产生不可预料的结果。换 句话说,hashCode()和 equals()应当基于那些可以唯一确定一个类的属性。这样的话, 如果两个 PoliticalCandidate 对象含有相同的 first 和 lastName,equals()会返回 true,并且它们的散列码也将相同。例 1-2 中的 hashCode()和 equals()方法仅涉及 firstName 和 lastName 属性。
还剩10页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享pdf获得金币 ] 2 人已下载

下载pdf

pdf贡献者

flyazdream

贡献于2014-01-25

下载需要 5 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf