失效链接处理 |
effective-java PDF 下载
本站整理下载:
提取码:xwhr
相关截图:
主要内容:
1. 考虑使用静态工厂方法替代构造方法
一个类允许客户端获取其实例的传统方式,是提供一个公共构造方法。 其实,还有另一种技术应该
成为每个程序员工具箱的一部分。 一个类可以提供一个简单的、只返回该类实例的公共静态工厂方法。
下面是一个 Boolean 简单的例子( 基本类型 boolean 的包装类)。 此方法将基本类型 boolean 转换
为 Boolean 对象引用:
注意,静态工厂方法与设计模式中的工厂方法模式不同[Gamma95]。本条目中描述的静态工厂方法
在设计模式中没有直接的等价。
类可以为其客户端提供静态工厂方法,而不是公共构造方法。提供静态工厂方法来代替提供公共构
造方法这种方式,有优点也有缺点。
静态工厂方法的一个优点是,与构造方法不同,它们是有名字的。 如果构造方法的参数本身并不描
述被返回的对象,则具有精心选择名称的静态工厂更易于使用,并且生成的客户端代码更易于阅读。 例
如,返回一个可能为素数的 BigInteger 的构造方法 BigInteger(int,int,Random) 可以更好地表
示为名为 BigInteger.probablePrime 的静态工厂方法。 (这个方法是在 Java 1.4 中添加的。)
一个类只能有一个给定签名的构造方法。 程序员知道通过提供两个仅仅在参数类型的顺序不同的构
造方法,来解决这个限制。 这是一个非常糟糕的主意。 对于这样的 API ,用户将永远不会记得哪个构
造方法是哪个,最终会错误地调用。 阅读使用这些构造方法的代码的人只有在参考类文档的情况下才知
道代码的作用。
因为静态工厂方法有名字,所以它们不会受到上述限制。在类中似乎需要具有相同签名的多个构造
方法的情况下,用静态工厂方法替换构造方法,并仔细选择名称来突出它们的差异。
静态工厂方法的第二个优点是,与构造方法不同,它们不需要每次调用时都创建一个新对象。 这允
许不可变类 (详见第 17 条)使用预先构建的实例,或者在构造时缓存实例,并反复分配它们以避免创
建不必要的重复对象。 Boolean.valueof(boolean) 方法说明了这种方法:它从不创建对象。这种技术
类似于 Flyweight 模式[Gamma95]。如果经常请求等价对象,那么它可以极大地提高性能,特别是在
创建它们的代价非常昂贵的情况下。
静态工厂方法在重复调用时,返回相同实例这个特点,可以让类在任何时候都能对实例保持严格的
控制。这样做的类被称为实例控制类( instance-controlled)。有很多理由足以让我们去我们编写实例
控制类。实例控制可以保证一个类是单例 的(详见第 3 条) 或不可实例化的 (详见第 4 条)。同时,它
允许一个不可变的值类 (详见第 17 条) 保证不存在两个相等但不相同的实例,也就是说当且仅当 a == b 时才有 a.equals(b) 。这是 Flyweight 模式的基础[Gamma95]。 Enum 类型 (详见第 34 条)
可以做到这点。
public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
静态工厂方法的第三个优点是,与构造方法不同,它们可以返回其返回类型的任何子类型的对象。
这为你在选择返回对象的类型时,提供了很大的灵活性。
这种灵活性的一个应用是, API 可以返回对象而不需要公开它的类。 以这种方式隐藏实现类,会使
API 非常紧凑。 这种技术适用于基于接口的框架(详见第 20 条),其中接口为静态工厂方法提供自然
返回类型。
在 Java 8 之前,接口不能有静态方法。根据约定,一个名为 Types 的接口的静态工厂方法被放入
一个不可实例化的伙伴类(companion class)(详见第 4 条) Types 类中。例如,Java 集合框架有
45 个接口的实用工具实现,提供不可修改的集合、同步集合等等。几乎所有这些实现都是通过静态工
厂方法在一个不可实例化的类 ( java.util.collections ) 中返回的。返回对象的类都是隐藏的。
Collections 框架 API 的规模要比它之前返回的 45 个单独的公共类要小得多,每个类在集合框架
中都有一个便利的实现。不仅精简了API,还减少了程序员为了使用 API而掌握的概念的数量和难度。程
序员知道返回的对象恰好有其接口指定的 API,因此不需要为实现类读阅读额外的类文档。此外,使用
这种静态工厂方法,需要客户端通过接口替代实现类,来引用返回的对象,这通常是良好的实践(详见
第 64 条)。
从 Java 8 开始,接口不能包含静态方法的限制被取消了,所以通常没有理由为接口提供一个不可实
例化的伴随类。 很多公开的静态成员应该放在这个接口本身。 但是,请注意,将这些静态方法的大部
分实现代码放在单独的包私有类中仍然是必要的。 这是因为 Java 8 要求所有接口的静态成员都是公共
的。 Java 9 允许私有静态方法,但静态字段和静态成员类仍然需要公开。
静态工厂的第四个优点是返回对象的类可以根据输入参数的不同而不同。 声明的返回类型的任何子
类都是允许的。 返回对象的类也可以随每次发布而不同。
EnumSet 类(详见第 36 条)没有公共构造方法,只有静态工厂。 在 OpenJDK 实现中,它们根据
底层枚举类型的大小返回两个子类中的一个的实例:大多数枚举类型具有 64 个或更少的元素,静态工
厂将返回一个 RegularEnumSet 实例, 底层是 long 类型;如果枚举类型具有六十五个或更多元素,
则工厂将返回一个 JumboEnumSet 实例,底层是 long 类型的数组。
这两个实现类的存在对于客户端而言是不可见的。 如果 RegularEnumSet 对于小的枚举类型不再
具有性能优势,则可以在未来版本中将其淘汰,且不会产生任何不良影响。 同样,如果可以证明添加
EnumSet 的更多的实现可以提高性能,那么在未来的版本可能就会这样做。 客户既不知道也不关心他
们从工厂返回的对象的类别; 他们只需要知道它是 EnumSet 的子类。
静态工厂的第五个优点是,在编写包含该方法的类时,返回的对象的类不需要存在。 这种灵活的静
态工厂方法构成了服务提供者框架的基础,比如 Java 数据库连接 API(JDBC)。服务提供者框架是提
供者实现服务的系统,并且系统使得实现对客户端可用,从而将客户端从实现中分离出来。
服务提供者框架中有三个基本组:服务接口,它表示实现;提供者注册 API,提供者用来注册实
现;以及服务访问 API,客户端使用该 API 获取服务的实例。服务访问 API 允许客户指定选择实现的标
准。在缺少这样的标准的情况下,API 返回一个默认实现的实例,或者允许客户通过所有可用的实现进
行遍历。服务访问 API 是灵活的静态工厂,它构成了服务提供者框架的基础。
服务提供者框架的一个可选的第四个组件是一个服务提供者接口,它描述了一个生成服务接口实例
的工厂对象。在没有服务提供者接口的情况下,必须对实现进行反射实例化(详见第 65 条)。在 JDBC
的情况下, Connection 扮演服务接口的一部分, DriverManager.registerDriver 提供程序注册
API、 DriverManager.getConnection 是服务访问 API, Driver 是服务提供者接口。
|