Author:Joshua Bloch
本书介绍了在Java编程中78条极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述,揭示了应该做什么,不应该做什么才能产生清晰、健壮和高效的代码。 本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。本书内容全面,结构清晰,讲解详细。可作为技术人员的参考用书。
Tags
Support Statistics
¥.00 ·
0times
Text Preview (First 20 pages)
Registered users can read the full content for free
Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.
Page
1
(This page has no text content)
Page
2
Table of Contents 1. 封面 1.1 2. 译者序 1.2 3. 序 1.3 4. 前言 1.4 5. 致谢 1.5 6. 第1章 引言 1.6 7. 第2章 创建和销毁对象 1.7 8. 第1条:考虑用静态工厂方法代替构造器 1.8 9. 第2条:遇到多个构造器参数时要考虑用构建器 1.9 10. 第3条:用私有的构造器或者枚举类型强化Singleton属 性 1.10 11. 第4条:通过私有构造器强化不可实例化的能力 1.11 12. 第5条:避免创建不必要的对象 1.12 13. 第6条:消除过期的对象引用 1.13 14. 第7条:避免使用终结方法 1.14 15. 第3章 对于所有对象都通用的方法 1.15 16. 第8条:覆盖equals时请遵守通用约定 1.16 17. 第9条:覆盖equals时总要覆盖hashCode 1.17
Page
3
18. 第10条:始终要覆盖toString 1.18 19. 第11条:谨慎地覆盖clone 1.19 20. 第12条:考虑实现Comparable接口 1.20 21. 第4章 类和接口 1.21 22. 第13条:使类的成员的可访问性最小化 1.22 23. 第14条:在公有类中使用访问方法而非公有域 1.23 24. 第15条:使可变性最小化 1.24 25. 第16条:复合优于继承 1.25 26. 第17条:要么为继承而设计,并提供文档说明,要么就 禁止继承 1.26 27. 第18条:接口优于抽象类 1.27 28. 第19条:接口只用于定义类型 1.28 29. 第20条:类层次优于标签类 1.29 30. 第21条:用函数对象表示策略 1.30 31. 第22条:优先考虑静态成员类 1.31 32. 第5章 泛型 1.32 33. 第23条:请不要在新代码中使用原生态类型 1.33 34. 第24条:消除非受检警告 1.34 35. 第25条:列表优先于数组 1.35 36. 第26条:优先考虑泛型 1.36
Page
4
37. 第27条:优先考虑泛型方法 1.37 38. 第28条:利用有限通配符来提升API的灵活性 1.38 39. 第29条:优先考虑类型安全的异构容器 1.39 40. 第6章 枚举和注解 1.40 41. 第30条:用enum代替int常量 1.41 42. 第31条:用实例域代替序数 1.42 43. 第32条:用EnumSet代替位域 1.43 44. 第33条:用EnumMap代替序数索引 1.44 45. 第34条:用接口模拟可伸缩的枚举 1.45 46. 第37条:用标记接口定义类型 1.46 Effective Java 中文版 第2版 (美)Joshua Bloch 著 杨春华 俞黎敏 译 我很希望10年前就拥有这本书。可能有人认为我不需要任何 Java方面的书籍,但是我需要这本书。 ——Java 之父 James Gosling 译者序 Java从诞生到日趋完善,经过了不断的发展壮大,目前全世界拥 有了成千上万的Java开发人员。如何编写出更清晰、更正确、更 健壮且更易于重用的代码,是大家所追求的目标之一。作为经典 Jolt获奖作品的新版书,它已经进行了彻底的更新,涵盖了自第1
Page
5
版之后所引入的Java SE 5和Java SE 6的新特性。作者探索了新 的设计模式和语言习惯用法,介绍了如何充分利用从泛型到枚 举、从注解到自动装箱的各种个性。本书的作者Joshua Bloch曾 经是Sun公司的杰出工程师,带领团队设计和实现过无数的Java 平台特性,包括JDK 5.0语言增强版和获奖的Java Collections Framework。他也是Jolt奖的获得者,现在担任Google公司的首 席Java架构师。他为我们带来了共78条程序员必备的经验法则: 针对你每天都会遇到的编程问题提出了最有效、最实用的解决方 案。 书中的每一章都包含几个“条目”,以简洁的形式呈现,自成独立 的短文,它们提出了具体的建议、对于Java平台精妙之处的独到 见解,并提供优秀的代码范例。每个条目的综合描述和解释都阐 明了应该怎么做、不应该怎么做,以及为什么。通过贯穿全书透 彻的技术剖析与完整的示例代码,仔细研读并加以理解与实践, 必定会从中受益匪浅。书中介绍的示例代码清晰易懂,也可以作 为日常工作的参考指南。 适合人群 本书不是针对初学者的,读者至少需要熟悉Java程序设计语言。 如果你连 equlas() 、 toString() 、 hashCode() 都还不了解的话, 建议先去看些优秀的Java入门书籍之后再来阅读本书。如果你现 在已经在Java开发方面有了一定的经验,而且想更加深入地了解 Java编程语言,成为一名更优秀、更高效的Java开发人员,那 么,建议你用心的研读本书。 内容形式 本书分为11章共78个条目,涵盖了Java 5.0/6.0的种种技术要 点。与第1版相比,本书删除了“C语言结构的替代”一章,增加了 Java 5所引入的“泛型”、“枚举和注解”各一章。数量上从57个条目 发展到了78个,不仅增加了23个条目,并对原来的所有资料都进 行了全面的修改,删去了一些已经过时的条目。但是,各章节没 有严格的前后顺序关系,你可以随意选择感兴趣的章节进行阅
Page
6
读。当然,如果你想马上知道第2版究竟有哪些变化,可以参阅 附录中第2版与第1版详细的对照情况。 本书重点讲述了Java 5所引入的全新的泛型、枚举、注解、自动 装箱、for-each循环、可变参数、并发机制,还包括对象、类、 类库、方法和序列化这些经典主题的全新技术和最佳实践,如何 避免Java语言中常被误解的细微之处:陷阱和缺陷,并重点关注 Java语言本身和最基本的类库: java.lang 、 java.util ,以及一 些扩展: java.util.concurrent 和 java.io 等等。 章节简介 第2章阐述何时以及如何创建对象,何时以及如何避免创建对 象,如何确保它们能够被适时地销毁,以及如何管理销毁之前必 须进行的所有清除动作。 第3章阐述对于所有对象都通用的方法,你会从中获知对 equals 、 hashCode 、 toString 、 clone 和 finalize 相当深入的分析, 从而避免今后在这些问题上再次犯错。 第4章阐述作为Java程序设计语言的核心以及Java语言的基本抽 象单元(类和接口),在使用上的一些指导原则,帮助你更好地 利用这些元素,设计出更加有用、健壮和灵活的类和接口。 第5章和第6章中分别阐述在Java 1.5发行版本中新增加的泛型 (Generic)以及枚举和注解的最佳实践,教你如何最大限度地享 有这些优势,又能使整个过程尽可能地简单化。 第7章讨论方法设计的几个方面:如何处理参数和返回值,如何 设计方法签名,如何为方法编写文档。从而在可用性、健壮性和 灵活性上有进一步的提升。 第8章主要讨论Java语言的具体细节,讨论了局部变量的处理、 控制结构、类库的使用、各种数据类型和用法,以及两种不是由 语言本身提供的机制(reflection和native method,反射机制和本 地方法)的用法,并讨论了优化和命名惯例。
Page
7
第9章阐述如何充分发挥异常的优点,可以提高程序的可读性、 可靠性和可维护性,以及减少使用不当所带来的负面影响。并提 供了一些关于有效使用异常的知道原则。 第10章阐述如何帮助你编写出清晰、正确、文档组织良好的并发 程序。 第11章阐述序列化方面的技术,并且有一项值得特别提及的特 性,就是序列化代理(serialization proxy)模式,它可以帮助你 避免对象序列化的许多缺陷。 举个例子,就序列化技术来讲,HTTP会话状态为什么可以被缓 存?RMI的异常为什么可以从服务端传递到客户端呢?GUI组件 为什么可以被发现、保存和恢复呢?是因为他们实现了 Serializable 接口吗?如果超类没有提供一个可访问的无参构造 器,它的子类可以被序列化吗?当一个实例采用默认的序列化形 式,并且给某些域标记为 transient ,那么当实例反序列化回来 后,这些标志为 transient 域的值各是些什么呢?……这些问题 如果你现在不能马上回答,或者不不能很确定,没有关系,仔细 阅读本书,你会对它们有更深入与透彻的理解。 技术范围 虽然本书是讨论更深层次的Java开发技术,讲述的内容深入,涉 及面又相当广泛,但是它并没有涉及图形用户界面编程、企业级 API以及移动设备方面的技术,不过在各个章节与条目中会不时 地讨论到其他相关的类库。 这是一本分享经验并指引你避免走弯路的经典著作,针对如何编 写搞笑、设计优良的程序提出了最实用、最权威的指导方针,是 Java开发人员案头上的一本不可或缺的参考书。 本书由我组织进行翻译,第1章到第8章由杨春花负责、我负责前 言、附录以及第9章到第11章的翻译,并负责本书所有章节的全 面审校。参与翻译和审校的还有:荣浩、邱庆举、万国辉、陆志
Page
8
平、姜法有、王琳、林仪明、凌家亮、李勇、师文丽、刘传飞、 王建旭、程旭文、罗兴、翟育明、黄华,在此深表感谢。 虽然我们在翻译过程中竭力追求信、达、雅,但限于自身水平, 也许仍有不足,还望各位读者不吝指正。关于本书的翻译和翻译 时采用的术语表以及相关的技术讨论大家可以访问我的博客 http://blog.csdn.net/YuLimin ,也可以发送邮件到 YuLimin@163.com与我交流。 在这里,我要感谢在翻译过程中一起讨论和帮助我的朋友们,他 们是:崔毅,郑晖,左轻侯,郭晓刚,满江红开放技术研究组织 创始人曹晓刚,Spring中文站创始人杨戈(Yanger), SpringSide创始人肖桦(江南白衣)和来自宝岛台湾的李日贵 (jini)、林康司(koji)、林信良(caterpillar),还有责任编辑 陈佳媛也为本书出版做了大量工作,在此再次深表感谢。 快乐分享,实践出真知,最后,祝大家能够像我一样在阅读中享 受本书带来的乐趣! Read a bit and take it out, then come back read some more. 俞黎敏 2008年11月 序 如果有一个同事这样对你说,“我的配偶今天晚上在家里制造了一 顿不同寻常的晚餐,你愿意来参加吗?”(Spouse of me, this night today manufactrures the unusual meal in a home. You will join?)这时候你脑子里可能会浮现起三件事情:第一,满脑子的 疑惑;第二,英语肯定不是这位同事的木鱼;第三,同事是在邀 请你参加他的家庭晚宴。 如果你曾经学习过第二种语言,并且尝试过在课堂之外使用这种 语言,你就该知道有三件事情是必须掌握的:这门语言的结构如 何(语法),如何命名你想谈论的事物(词汇),以及如何以惯
Page
9
用和高效的方式来表达日常的事物(用法)。在课堂上大多只涉 及前面两点,当你是出浑身解数想让对方明白你的意思时,常常 会发现当地人对你的表述忍俊不禁。 程序设计语言也是如此。你需要理解语言的核心,它是面向算法 的,还是面向函数的,或者是面向对象的?你需要知道词汇表: 标准类库提供了哪些数据结构、操作和功能(Facility)?你还需 要熟悉如何用习惯和高效的方式来构建代码。关于程序设计语言 的书籍通常只是设计前面两点,或者只是蜻蜓点水般地介绍一下 用法。也许是因为前面两点比较容易编写。语法和词汇是语言本 身固有的特性,但是,用法则反映了使用这门语言的群体的特 征。 例如,Java程序设计语言是一门支持单继承的面向对象程序设计 语言,在每个方法的内部,它也支持命令式的(面向语句的, Statement-Oriented)编码风格。Java类库提供了对图形显示、 网络、分布式计算和安全性的支持。但是,如何把这门语言以最 佳的方式运用到实践中呢? 还有一点:程序与口语中的句子以及大多数书籍和杂志都不同, 它会醉着时间的推移而发生变化。仅仅编写出能够有效地工作并 且能够被别人理解的代码往往是不够的,我们还必须把代码组织 成易于修改的形式。针对某个任务可能会有10种不同的编码方 法,而在这10中方法中,有7中方法是笨拙的、低效的或者是难 以理解的。而在剩下的3种编码方法中,哪一种会是最接近该任 务的下一年度发行版本的代码呢? 目前有大量的书籍可以供你学习Java程序设计语言的语法,包括 《The Java Programming Language》[Arnold05](作者 Arnold、Gosling和Holmes),以及《The Java Language Specification》[JLS](作者Gosling、Joy和Bracha)。同样,与 Java程序设计语言相关的类库和API的书籍也不少。 本书解决了你的第三种需求:习惯和高效的用法。作者Joshua Bloch在Sun公司多年来一直从事Java语言的扩展、实现和使用的 工作;他还大量地阅读了其他人的代码,包括我的代码。他在本
Page
10
书中提出了许多很好的建议,他系统地把这些建议组织起来,旨 在告诉读者如何更好地构造代码以便它们能工作得更好,也便于 其他人能够理解这些代码,便于将来对代码进行修改和改善的时 候不至于那么头疼。甚至,你的程序也会因此而变得更加令人愉 悦、更加优美和雅致。 Guy L. Steele Jr. Burlington, Massachusetts 2001年4月 前言 自从我于2001年写了本书的第1版之后,Java平台又发生了很多 变化,是该出第2版的时候了。Java 5中最为重要的变化是增加了 泛型、枚举类型、注解、自动装箱和for-each循环。其次是增加 了新的并发类库: java.util.concurrent 。我和Gilad Bracha一起, 有幸带领团队设计了最新的语言特性。我还有幸参加了设计和开 发并发类库的团队,这个团队由Doug Lea领导。 Java平台中另一个大的变化在于广泛采用了现代IDE(Integrated Development Environment),例如Eclipse、IntelliJ IDEA和 NetBeans,以及静态分析工具的IDE,如FindBugs。虽然我还未 参与到这部分工作,但已经从中受益匪浅,并且很清楚它们对 Java开发体验所带来的影响。 2004年,我离开Sun公司到了Google公司工作,但在过去的4年 中,我仍然继续参与Java平台的开发,在Google公司和JCP (Java Community Process)的大力帮助下,继续并发和集合 API的开发。我还有幸利用Java平台去开发供Google内部使用的 类库。现在我了解了作为一名用户的感受。 我在2001年编写第1版的时候,主要目的是与读者分享我的经 验,便于让大家能够避免我所走过的弯路,是大家更容易成功。 新版仍然采用大量来自Java平台类库的真实范例。
Page
11
第1版所带来的反应远远超出了我最大的语气。我在收集所有新 的资料以使本书保持最新时,尽可能地保持了资料的真实。毫无 疑问,本书的篇幅肯定会增加,从57个条目发展到了78个。我不 仅增加了23个条目,并且修改了原来的所有资料,并删去了一些 已经过时的条目。在附录中,你可以看到本书中的内容与第1版 的内容的对照情况。 在第1版的前言中我说过:Java程序设计语言和它的类库非常有 益于代码质量和效率的提高,并且使得用Java进行编码成为一种 乐趣。Java 5和6发行版本中的变化是好事,也使得Java平台日 趋完善。现在这个平台比2001年的要大得多,也复杂得多,但是 一旦掌握了使用新特性的模式和习惯用法,它们就会使你的程序 变得更完美,使你的工作变得更轻松。我希望第2版能够体现出 我对Java平台持续的热情,并将这种热情传递给你,帮助你更加 高效和愉快地使用Java平台及其新的特性。 Joshua Bloch San Jose, California 2008年4月 致谢 我要感谢本书第1版的读者给予本书如此热情的好评,感谢他们 将书中的理念铭记于心,感谢他们让我知道该书给他们以及他们 的工作带来了怎样积极的影响。我感谢许多教授在教学中采用了 本书,感谢许多开发团队应用了本书。 我要感谢Addison-Wesley的整个团队,感谢他们的诚恳、耐心, 以及压力之下所体现出来的从容。编辑Greg Doench自始至终保 持镇定自若:他是一名优秀的编辑,同时也是一位完美的绅士。 产品经理Julie Nahil具备了产品经理应该具备的一切:勤奋、敏 捷、训练有素,且待人和气。编审Barbara Wood一丝不苟,富有 鉴赏能力。
Page
12
我有幸再一次得到了所能想到的最佳审核团队的支持,我真诚地 感谢他们中的每一位。核心团队负责审核每一个章节,他们包 括:Lexi Baugher、Cindy Bloch、Beth Bottos、Joe Bowbeer、 Brian Goetz、Tim Halloran、Brian Kernighan、Rob Konigsberg、Tim Peierls、Bill Pugh、Yoshiki Shibata、Peter Stout、Peter Weinberger以及Frank Yellin。其他审核人员包括: Pablo Bellver、Dan Bloch、Dan Bornstein、Kevin Bourrillion、 Martin Buchholz、Joe Darcy、Neal Gafter、Laurence Gonsalves、Aaron Greenhouse、Barry Hayes、Peter Jones、 Angelika Langer、Doug Lea、Bob Lee、Jeremy Manson、Tom May、Mike McCloskey、Andriy Tereshchenko以及Paul Tyma。 这些审核人员再次提出了大量的建议,使本书得到了极大的改 善,也让我避免了诸多尴尬。剩下的任何错误都是我自己的责 任。 我要特别感谢Doug Lea和Tim Peierls,他们成了书中许多理念的 倡导者。Doug和Tim为本书毫不吝惜地奉献了他们的时间和学 识。 我要感谢我在Google公司的经历Prabha Krishna,感谢她持续不 断的支持和鼓励。 最后,我要感谢我的妻子Cindy Bloch,她鼓励我写作,阅读了初 稿中的每个条目,用Framemaker帮我排版,为我编写索引,在 我写作的时候一直对我十分宽容。 引言 TBD 第2章 创建和销毁对象 本章的主题是创建和销毁对象:何时以及如何创建对象,何时以 及如何避免创建对象,如何确保它们能够适时地销毁,以及如何 管理对象销毁之前必须进行的各种清理动作。
Page
13
第1条:考虑使用静态工厂方法代替构造器 对于类而言,为了让客户端获取它自身的一个实例,最常用的方 法就是提供一个公有的构造器。还有一种方法,也应该在每个程 序员的工具箱中占有一席之地。类可以提供一个公有的 静态工厂 方法 (static factory method),它只是一个返回类的实例的静态 方法。下面是一个来自 Boolean (基本类型 boolean 的包装类) 的简单示例。这个方法将 boolean 基本类型值转换成了一个 Boolean 对象引用: public static Boolean valueOf ( boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; } 注意,静态工厂方法与设计模式[Gamma95,p.107]中的工厂方 法模式不同。本条目中所指的静态工厂方法并不直接对应于设计 模式中的工厂方法。 类可以通过静态工厂方法来提供它的客户端,而不是通过构造 器。提供静态工厂方法而不是公有的构造器,这样可以具有几大 优势。 静态工厂方法与构造器不同的第一大优势在于,它具有名称。 如 果构造器的参数本身没有确切地描述正被返回的对象,那么具有 适当名称的静态工厂会更容易使用,产生的客户端代码也更易于 阅读。例如,构造器 BigInteger(int, int, Random) 返回的 BigInteger 可能为素数,如果用名为 BigInteger.probablePrime 的静态工厂方法 来表示,显然更为清楚。(1.4的发行版本中最终增加了这个方 法。) 一个类只能有一个带有指定签名的构造器。编程人员通常知道如 何避开这一限制:通过提供两个构造器,它们的参数列表只在参 数类型的顺序上有所不同。实际上这并不是个好主意。面对这样
Page
14
的API,用户永远也记不住该用哪个构造器,结果常常会调用错 误的构造器。并且,读到使用了这些构造器的代码时,如果没有 参考类的文档,往往不知所云。 静态工厂方法与构造器不同的第二大优势在于,不必在每次调用 它们的时候都创建一个新对象。 这使得不可变类(见第15条)可 以使用预先构建好的实例,或者将构建好的实例缓存起来,进行 重复利用,从而避免创建不必要的重复对象。 Boolean.valueOf(boolean) 方法说明了这项技术:它从来不创建对 象。这种方法类似于 Flyweight模式 [Gamma95,p.195]。如果程序 经常创建相同的对象,并且创建对象的代价很高,则这项技术可 以极大地提升性能。 静态工厂方法能够为重复的调用返回相同的对象,这样有助于类 总能严格控制在某个时刻哪些实例应该存在。这种类被称作 实例 受控的类 (instance-controlled)。编写实例受控类有几个原 因。实例受控使得类可以确保它是一个 Singleton (见第3条)或 者是不可实例化的(见第4条)。它还使得不可变的类(见第15 条)可以确保不会存在两个相等的实例,即当且仅当 a==b 的时 候才有 a.equals(b) 为 true 。如果类保证了这一点,它的客户端 就可以使用 == 操作符来代替 equals(Object) 方法,这样可以提升 性能。枚举( enum )类型(见第30条)保证了这一点。 静态工厂方法与构造器不同的第三大优势在于,它们可以返回原 返回类型的任何子类型的对象。 这样我们在选择返回对象的类时 就有了更大的灵活性。 这种灵活性的一种应用是,API可以返回对象,同时又不会使对 象的类变成公有的。以这种方式隐藏实现类会使API变得非常简 洁。这项技术适用于基于接口的框架(interface-based framework,见第18条),因为在这种框架中,接口为静态工厂 方法提供了自然返回类型。接口不能有私有方法,因此按照惯 例,接口 Type 的静态工厂方法放在一个名为 Types 的不可实例 化的类(见第4条)中。
Page
15
例如,Java Collections Framework的集合接口有32个便利实 现,分别提供了不可修改的集合、同步集合等等。几乎所有这些 实现都通过静态工厂方法在一个不可实例化的类( java.util.Collections )中导出。所有返回对象都的类是非公有 的。 现在的Java Collections Framework API比导出32个独立公有类 的那种实现方式要小得多,每种便利实现都对应一个类。这不仅 仅是指API数量上的减少,也是概念意义上的减少。用户知道, 被返回的对象是由相关的接口精确指定的,所以他们不需要阅读 有关的文档。使用这种静态工厂方法是,甚至要求客户端通过接 口来引用被返回的对象,而不是通过它的实现类来引用被返回的 对象,这是一种良好的习惯(见第52条)。 共有的静态工厂方法所返回的对象的类不仅可以是非公有的,而 且该类还可以随着每次调用而发生变化,这取决于静态工厂方法 的参数值。只要是已声明的返回类型的子类型,都是允许的。为 了提升软件的可维护性和性能,返回对象的类也可能随着发行版 本的不同而不同。 发新版本1.5中引入的类 java.util.EnumSet (见第32条)没有公有 构造器,只有静态工厂方法。它们返回两种实现烈性之一,具体 则取决于底层枚举类型的大小;如果它的元素有64个或者更少, 就像大多数枚举类型一样,静态工厂方法就会返回一个 RegularEnumSet 实例,用 单个long 进行支持;如果枚举类型有65个 或者更多元素,工厂就返回 JumboEnumSet 实例,用 long数组 进行 支持。 这两个实现类的存在对于客户端来说是不可见的。如果 RegularEnumSet 不能再给小的枚举类型提供性能优势,就可能从未 来的发行版本中将它删除,不会造成不良的影响。同样地,如果 事实证明对性能有好处,也可能在未来的发行版本中添加第三甚 至第四个 EnumSet 实现。客户端永远不知道也不关心他们从工厂 方法中得到的对象的类;他们只关心它是 EnumSet 的某个子类即 可。
Page
16
静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法 的类时可以不必存在。这种灵活的静态工厂方法构成了 服务提供 者框架 (Service Provider Framework)的基础,例如JDBC (Java数据库连接, Java Databse Connectivity)API。服务提 供者框架是指这样一个系统:多个服务提供者实现一个服务,系 统为服务提供者的客户端提供多个实现,并把他们从多个实现中 解耦出来。 服务提供者框架中有三个重要的组件:服务接口(Service Interface),这是提供者实现的;提供者注册API(Provider Registration API),这是系统用来注册实现,让客户端访问它们 的;服务访问API(Service Access API),是客户端用来获取服 务的实例的。服务访问API一般允许但是不要求客户端指定某种 选择提供者的条件。如果没有这样的规定,API就会返回默认实 现的一个实例。服务访问API是“灵活的静态工厂”,它构成了服务 提供者框架的基础。 服务提供者框架的第四个组件是可选的:服务提供者接口 (Service Provider Interface),这些提供者负责创建其服务实现 的实例。如果没有服务提供者接口,实现就按照类名进行注册, 并通过反射方式进行实例化(见第53条)。对于JDBC来说, Connection 就是它的服务接口, DriverManager.registerDriver 是提供 者注册API, DriverManager.getConnection 是服务访问API, Driver 就是服务提供者接口。 服务提供者框架模式有着无数种变体。例如,服务访问API可以 利用适配器(Adapter)模式[Gamma95,p.139],返回比提供者 需要的更丰富的服务接口。下面是一个简单的实现,包含一个服 务提供者接口和一个默认提供者: // Service provider framework sketch // Service interface
Page
17
public interface Service { ... // Service-specific methods go here } // Service provider interface public interface Provider { Service newService () ; } // Noninstantiable class for service registration and access public class Services { private Services () {} // Prevents instantiation (Item 4) // Maps service names to services private static final Map<String, Provider> providers = new ConcurrentHasMap<String, Provider>(); public static final String DEFAULT_PROVIDER_NAME = "<def>" ; // Provider registration API
Page
18
public static void registerDefaultProvider (Provider p) { registerProvider(DEFAULT_PROVIDER_NAME, p); } public static void registerProvider (String name, Provider p) { providers.put(name, p); } // Service access API public static Service newInstance () { return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance (String name) { Provider p = providers.get(name); if (p == null ) throw new IllegalArgumentException( "No provider registered with name: " + name);
Page
19
return p.newService(); } } 静态工厂方法的第四大优势在于,在创建参数化类型实例的 时候,它们使代码变得更加简洁。 遗憾的是,在调用参数化 类的构造器时,即使类型参数很明显,也必须指明。这通常 要求你接连两次提供类型参数: Map<String, List<String>> m = new HashMap<String, List<String>>(); 随着类型参数变得越来越长,越来越复杂,这一冗长的说明 也很快变得痛苦起来。但是有了静态工厂方法,编译器就可 以替你找到类型参数。z和被称作类型推到(type inference)。例如,假设 HashMap 提供了这个静态工厂: public static <K, V> HashMap<K, V> newInstance () { return new HashMap<K, V>(); } 你就可以用下面这句简洁的代码代替上面这段繁琐的声明: Map<String, List<String>> m = HashMap.newInstance(); 总有一天,Java能够在构造器调用以及方法调用中执行这种 类型推到,但到发行版本1.6为止暂时还无法这么做。
Page
20
遗憾的是,到发行版本1.6为止,标准的集合实现如 HashMap 并没有工厂方法,但是可以把这些方法放在你自己的工具类 中。更重要的是,可以把这样的静态工厂放在你自己的参数 化的类中。 静态工厂方法的主要缺点在于,类如果不含有公有的或者受 保护的构造器,就不能被子类化。 对于公有的静态工厂所返 回的非公有类,也同样如此。例如,要想将Collections Framework中的任何方便的实现类子类化,这是不可能的。 但是这样也许会因祸得福,因为它鼓励程序员使用复合 (composition),而不是继承(见第16条)。 静态工厂方法的第二个缺点在于,它们与其他的静态方法实 际上没有任何区别。 在API文档中,它们没有像构造器那样 在API文档中明确标识出来,因此,对于提供了静态工厂方 法而不是构造器的类来说,要想查明如何实例化一个类,这 是非常困难的。Javadoc工具总有一天会注意到静态工厂方 法。同时,你通过在类或者接口注释中关注静态工厂,并遵 守标准的命名习惯,也可以弥补这一劣势。下面是静态工厂 方法的一些惯用名称: valueOf —— 不太严格地讲,该方法返回的实例与它的 参数具有相同的值。这样的静态工厂方法实际上是类型 转化方法。 of —— valueOf 的一种更为简洁的替代,在 EnumSet (见第32条)中使用并流行起来。 getInstance —— 返回的实例是通过方法的参数来描述 的,但是不能够说与参数具有同样的值。对于 Singleton 来说,该方法没有参数,并返回唯一的实例。 newInstance —— 像 getInstance 一样,但 newInstance 能 够确保返回的每个实例都与所有其他实例不同。 getType —— 像 getInstance 一样,但是在工厂方法处于 不同的类中的时候使用。 Type 表示工厂方法所返回的 对象类型。 newType —— 像 newInstance 一样,但是在工厂方法处于 不同的类中的时候使用。 Type 表示工厂方法所返回的
Comments 0
Loading comments...
Reply to Comment
Edit Comment