文章作者:Tyan
博客:noahsnail.com
Chapter 2 Creating and Destroying Objects
THIS chapter concerns creating and destroying objects: when and how to create them, when and how to avoid creating them, how to ensure they are destroyed in a timely manner, and how to manage any cleanup actions that must precede their destruction.
这章是关于创建和销毁对象的:什么时候怎样创建它们,什么时候怎样避免创建它们,怎样确保它们被及时的销毁,怎么管理任何清理操作,清理操作必须在对象销毁之前。
Item 1: Consider static factory methods instead of constructors
Item 1: 考虑用静态工厂方法代替构造函数
The normal way for a class to allow a client to obtain an instance of itself is to provide a public constructor. There is another technique that should be a part of every programmer’s toolkit. A class can provide a public static factory method
, which is simply a static method that returns an instance of the class. Here’s a simple example from Boolean (the boxed primitive class for the primitive type boolean). This method translates a boolean primitive value into a Boolean object reference:
一个类允许客户获得它本身的一个实例通常的方式是提供一个公有的构造函数。还有另一种技术应该成为每个程序员工具箱中的一部分。一个类可以提供一种公有的static factory method
,static factory method
是一种简单的静态方法,它会返回一个类的实例。这有一个来自Boolean(基本类型boolean的封装类)的简单例子。这个方法将一个布尔值转成Boolean对象的引用:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
Note that a static factory method is not the same as the Factory Method
pattern from Design Patterns
[Gamma95, p. 107]. The static factory method described in this item has no direct equivalent in Design Patterns.
注意静态工厂方法与Design Patterns
中的Factory Method
是不同的。这个条目中描述的静态工厂方法与设计模式中的工厂方法是不等价的。
A class can provide its clients with static factory methods instead of, or in addition to, constructors. Providing a static factory method instead of a public constructor has both advantages and disadvantages.
一个类可以为它的客户提供静态工厂方法来代替构造函数,或者除了构造函数之外再提供一个静态工厂方法。提供静态工厂方法代替公有构造函数既有优点也有缺点。
One advantage of static factory methods is that, unlike constructors, they have names. If the parameters to a constructor do not, in and of themselves, describe the object being returned, a static factory with a well-chosen name is easier to use and the resulting client code easier to read. For example, the constructor BigInteger(int, int, Random)
, which returns a BigInteger
that is probably prime, would have been better expressed as a static factory method named BigInteger.probablePrime
. (This method was eventually added in the 1.4 release.)
与构造函数相比,静态工厂方法的第一个优势是它们有名字。如果构造函数的参数本身不能描述返回的对象,具有合适名字的静态工厂是更容易使用的,并且产生的客户端代码更易读。例如,构造函数BigInteger(int, int, Random)
返回一个BigInteger
,这个BigInteger
可能是一个素数,使用名字为BigInteger.probablePrime
的静态工厂方法来表示会更好。(这个方法最终在1.4版本被引入。)
A class can have only a single constructor with a given signature. Programmers have been known to get around this restriction by providing two constructors whose parameter lists differ only in the order of their parameter types. This is a really bad idea. The user of such an API will never be able to remember which constructor is which and will end up calling the wrong one by mistake. People reading code that uses these constructors will not know what the code does without referring to the class documentation.
一个类只能有一个具有指定签名的构造函数。程序员知道怎样规避这个限制:通过提供两个构造函数,它们仅在参数列表类型的顺序上有所不同。这真的是一个坏主意。使用这种API的用户永远不能记住哪一个构造函数是哪一个,最后会无意中调用错误的构造函数。使用这些构造函数的人在读代码时如果没有类的参考文档将不知道代码要做什么。
Because they have names, static factory methods don’t share the restriction discussed in the previous paragraph. In cases where a class seems to require multiple constructors with the same signature, replace the constructors with static factory methods and carefully chosen names to highlight their differences.
因为静态工厂方法有名字,因此它们不会有上一段讨论的那种限制。当一个类似乎需要多个具有相同签名的构造函数时,用静态工厂方法代替构造函数,通过仔细选择工厂方法的名字来突出它们的不同。
A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked. This allows immutable classes (Item 15) to use preconstructed instances, or to cache instances as they’re constructed, and dispense them repeatedly to avoid creating unnecessary duplicate objects. The Boolean.valueOf(boolean)
method illustrates this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95, p. 195]. It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create.
与构造函数相比,静态工厂方法的第二个优势是当调用静态工厂方法时不要求每次都创建一个新的对象。这允许不可变类(Item 15)使用预创建的实例,或缓存构建好的实例,通过重复分发它们避免创建不必要的重复对象。Boolean.valueOf(boolean)
方法阐明了这个技术:它从未创建对象。这项技术与Flyweight模式类似[Gamma95, p. 195]。如果经常请求相同的对象,它能极大的提升性能,尤其是在创建对象的代价较昂贵时。
The ability of static factory methods to return the same object from repeated invocations allows classes to maintain strict control over what instances exist at any time. Classes that do this are said to be instance-controlled. There are several reasons to write instance-controlled classes. Instance control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable (Item 4). Also, it allows an immutable class (Item 15) to make the guarantee that no two equal instances exist: a.equals(b)
if and only if a==b
. If a class makes this guarantee, then its clients can use the ==
operator instead of the equals(Object)
method, which may result in improved performance. Enum types (Item 30) provide this guarantee.
静态工厂方法能从重复的调用中返回相同的对象,在任何时候都能使类严格控制存在的实例。这些类被称为控制实例。编写控制实例类是有一些原因的。实例控制允许一个类保证它是一个单例(Item 3)或不可实例化的(Item 4)。它也允许一个不变的类(Item 15)保证不存在两个相等的实例:a.equals(b)
当且仅当a==b
。如果一个类保证了这一点,它的客户端可以使用==
操作符代替equals(Object)
方法,这可能会导致性能的提升。Enum类型(Item 30)保证了这一点。
A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type. This gives you great flexibility in choosing the class of the returned object.
与构造函数相比,静态工厂方法的第三个优势是它们能返回它们的返回类型的任意子类型的对象。这样在选择返回对象的类时有了更大的灵活性。
One application of this flexibility is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks (Item 18), where interfaces provide natural return types for static factory methods.Interfaces can’t have static methods, so by convention, static factory methods for an interface named Type
are put in a noninstantiable class (Item 4) named Types
.
灵活性的一个应用是API能返回对象而不必使它们的类变成公有的。通过这种方式中隐藏实现类会有一个更简洁的API。这项技术适用于基于接口的框架(Item 18),接口为静态工厂方法提供了自然的返回类型。接口不能有静态方法,因此按惯例,命名为Type
的接口的静态工厂方法被放在一个命名为Types
的不可实例化的类中(Item 4)。
For example, the Java Collections Framework has thirty-two convenience implementations of its collection interfaces, providing unmodifiable collections, synchronized collections, and the like. Nearly all of these implementations are exported via static factory methods in one noninstantiable class (java.util.Collections
). The classes of the returned objects are all nonpublic.
例如,Java集合框架有三十二个集合接口的便利实现,提供了不可修改的集合,同步集合等等。几乎所有的这些实现都是通过静态工厂方法导出在一个不可实例化的类中(java.util.Collections
)。返回对象的类都是非公有的。
The Collections Framework API is much smaller than it would have been had it exported thirty-two separate public classes, one for each convenience implementation. It is not just the bulk of the API that is reduced, but the conceptual weight. The user knows that the returned object has precisely the API specified by its interface, so there is no need to read additional class documentation for the implementation classes. Furthermore, using such a static factory method requires the client to refer to the returned object by its interface rather than its implementation class, which is generally good practice (Item 52).
集合框架API比它导出的三十二个分开的公有类更小,每一个便利实现对应一个类。它不仅仅是API的数量在减少,还是概念上意义上的减少。用户知道返回的对象含有接口指定的精确API,因此不需要阅读额外的实现类的文档。此外,使用这样的静态工厂方法需要客户端使用接口引用返回的对象而不是使用它的实现类,这通常是最佳的实践(Item 52)。
Not only can the class of an object returned by a public static factory method be nonpublic, but the class can vary from invocation to invocation depending on the values of the parameters to the static factory. Any class that is a subtype of the declared return type is permissible. The class of the returned object can also vary from release to release for enhanced software maintainability and performance.
不仅公有静态工厂方法返回对象的类可以是非公有的,而且这个类还可以随着调用静态工厂时输入的参数值的变化而变化。声明的返回值类型的任何子类都是可以的。为了增强软件的可维护性及性能,返回值对象的类也可以随着发布版本的变化而变化。
The class java.util.EnumSet
(Item 32), introduced in release 1.5, has no public constructors, only static factories. They return one of two implementations, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet
instance, which is backed by a single long
; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet
instance, backed by a long array.
在1.5版本中引入类java.util.EnumSet
(Item 32),它没有公有的构造函数,只有静态工厂方法。根据枚举类型的大小,静态工厂方法返回两个实现中的一个,枚举类型的分类:如果枚举类型中有六十四个元素或更少,与大多数枚举类型一样,静态工厂返回一个RegularEnumSet
实例,由单个的long
支持;如果枚举类型中有六十五个元素或更多,静态工厂方法返回一个JumboEnumSet
实例,由long[]
支持。
The existence of these two implementation classes is invisible to clients. If RegularEnumSet
ceased to offer performance advantages for small enum types, it could be eliminated from a future release with no ill effects. Similarly, a future release could add a third or fourth implementation of EnumSet
if it proved beneficial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet
.
现有的两个实现类对于客户端是不可见的。如果RegularEnumSet
对于较少数量的枚举类型没有提供性能优势,那么在将来的版本中将其移除不会任何影响。同样地,如果新的EnumSet
实现在性能上更有优势,在将来的版本中添加EnumSet
的第三或第四个实现也不会有任何影响。客户端不知道也不关心它们从工厂方法中得到的对象所属的类;它们只关心它是EnumSet
的某个子类。
The class of the object returned by a static factory method need not even exist at the time the class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, such as the Java Database Connectivity API (JDBC). A service provider framework is a system in which multiple service providers implement a service, and the system makes the implementations available to its clients, decoupling them from the implementations.
在编写静态工厂方法所属的类时,静态工厂方法返回的对象所属的类可以不必存在。这种灵活的静态工厂方法形成了服务提供者框架的基础,例如Java数据库链接API(JDBC)。服务提供者框架是一个系统:多个服务提供者实现一个服务,系统为客户端提供服务的多个实现,使客户端与服务实现解耦。
There are three essential components of a service provider framework: a service interface, which providers implement; a provider registration API, which the system uses to register implementations, giving clients access to them; and a service access API, which clients use to obtain an instance of the service. The service access API typically allows but does not require the client to specify some criteria for choosing a provider. In the absence of such a specification, the API returns an instance of a default implementation. The service access API is the “flexible static factory” that forms the basis of the service provider framework.
服务提供者框架有三个基本的组件:服务接口,提供者实现;提供者注册API,系统用来注册实现,使客户端能访问它们;服务访问API,客户端用来得到服务实例。服务访问API通常允许但不要求客户端指定一些选择提供者的规则。在没有指定的情况下,API返回一个默认的实现实例。服务访问API是"灵活的静态工厂",其形成了服务提供者框架的基础。
An optional fourth component of a service provider framework is a service provider interface, which providers implement to create instances of their service implementation. In the absence of a service provider interface, implementations are registered by class name and instantiated reflectively (Item 53). In the case of JDBC, Connection
plays the part of the service interface, DriverManager.registerDriver
is the provider registration API, DriverManager.getConnection
is the service access API, and Driver
is the service provider interface.
服务提供者框架的第四个可选组件是服务提供者接口,服务提供者通过实现这个接口来创建服务实现的实例。在没有服务提供者接口的情况下,服务实现通过类名进行注册,通过反射来进行实例化(Item 53)。在JDBC的案例中,Connection
是服务接口,DriverManager.registerDriver
是提供者注册API,DriverManager.getConnection
服务访问API,Driver
是服务提供者接口。
There are numerous variants of the service provider framework pattern. For example, the service access API can return a richer service interface than the one required of the provider, using the Adapter pattern [Gamma95, p. 139]. Here is a simple implementation with a service provider interface and a default provider:
服务提供者框架模式有许多变种。例如,服务访问API通过使用适配器模式[Gamma95, p. 139],能返回比提供者需要的更更丰富的服务接口。下面是服务提供者接口的一个简单实现和默认的提供者:
// Service provider framework sketch
// Service interface
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 ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
// Provider registration API
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);
return p.newService();
}
}
A fourth advantage of static factory methods is that they reduce the verbosity of creating parameterized type instances. Unfortunately, you must specify the type parameters when you invoke the constructor of a parameterized class even if they’re obvious from context. This typically requires you to provide the type parameters twice in quick succession:
静态工厂方法的第四个优势是它们降低了创建参数化类型实例的冗长性。遗憾的是,当你调用参数化类的构造函数时,你必须指定类型参数,即使它们在上下文中是非常明显的。这通常需要你紧接着提供两次类型参数:
Map<String, List<String>> m =
new HashMap<String, List<String>>();
This redundant specification quickly becomes painful as the length and complexity of the type parameters increase. With static factories, however, the compiler can figure out the type parameters for you. This is known as type inference. For example, suppose that HashMap
provided this static factory:
随着类型参数长度和复杂性的增加,这个冗长的说明很快就让人变得很痛苦。但是使用静态工厂的话,编译器可以为你找出类型参数。这被称为类型推导。例如,假设HashMap
由这个静态工厂提供:
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
Then you could replace the wordy declaration above with this succinct alternative:
你可以将上面冗长的声明用下面简洁的形式去替换:
Map<String, List<String>> m = HashMap.newInstance();
Someday the language may perform this sort of type inference on constructor invocations as well as method invocations, but as of release 1.6, it does not.
某一天,Java语言可能在构造函数调用上也有与方法调用类似的类型推导,但到发行版本1.6为止,它一直没有。
Unfortunately, the standard collection implementations such as HashMap
do not have factory methods as of release 1.6, but you can put these methods in your own utility class. More importantly, you can provide such static factories in your own parameterized classes.
遗憾的是,但到发行版本1.6为止,标准集合实现例如HashMap
没有工厂方法,但你可以把这些方法放到你自己的工具类力。更重要的是,你可以在你自己的参数化类里提供这样的静态工厂。
The main disadvantage of providing only static factory methods is that classes without public or protected constructors cannot be subclassed. The same is true for nonpublic classes returned by public static factories. For example, it is impossible to subclass any of the convenience implementation classes in the Collections Framework. Arguably this can be a blessing in disguise, as it encourages programmers to use composition instead of inheritance (Item 16).
只提供静态工厂方法的主要缺点是没有公有或保护构造函数的类不能进行子类化。公有静态工厂返回的非公有类同样如此。例如,不可能子类化集合框架中的这些便利实现类。可以说这是因祸得福,因为它鼓励程序员使用组合来代替继承(Item 16)。
A second disadvantage of static factory methods is that they are not readily distinguishable from other static methods. They do not stand out in API documentation in the way that constructors do, so it can be difficult to figure out how to instantiate a class that provides static factory methods instead of constructors. The Javadoc tool may someday draw attention to static factory methods. In the meantime, you can reduce this disadvantage by drawing attention to static factories in class or interface comments, and by adhering to common naming conventions. Here are some common names for static factory methods:
valueOf
— Returns an instance that has, loosely speaking, the same value as its parameters. Such static factories are effectively type-conversion methods.of
— A concise alternative tovalueOf
, popularized byEnumSet
(Item 32).getInstance
— Returns an instance that is described by the parameters but cannot be said to have the same value. In the case of a singleton,getInstance
takes no parameters and returns the sole instance.newInstance
— LikegetInstance
, except thatnewInstance
guarantees that each instance returned is distinct from all others.getType
— LikegetInstance
, but used when the factory method is in a different class.Type
indicates the type of object returned by the factory method.newType
— LikenewInstance
, but used when the factory method is in a different class.Type
indicates the type of object returned by the factory method.
静态工厂方法的第二个缺点是它们不能很容易的与其它静态方法进行区分。它们不能像构造函数那样在API文档中明确标识出来,因此很难弄明白怎样实例化一个提供静态工厂方法代替构造函数的类。Javadoc工具可能某一天会关注静态工厂方法。同时,你可以通过在类中或接口注释中注意静态工厂和遵循通用命名约定来减少这个劣势。下面是静态工厂方法的一些常用命名:
valueOf
— 不严格地说,返回一个与它的参数值相同的一个实例。这种静态工厂是有效的类型转换方法。of
—valueOf
的一种简洁替代方法,通过EnumSet
(Item 32)得到普及。getInstance
— 返回一个通过参数描述的实例,但不能说是相同的值。在单例情况下,getInstance
没有参数并且返回唯一的一个实例。newInstance
— 除了newInstance
保证每个返回的实例都是与其它的实例不同之外,其它的类似于getInstance
,getType
— 类似于getInstance
,当静态工厂方法在不同的类中时使用。Type
表示静态工厂方法返回的对象类型。newType
— 类似于newInstance
,当静态工厂方法在不同的类中时使用。Type
表示静态工厂方法返回的对象类型。
In summary, static factory methods and public constructors both have their uses, and it pays to understand their relative merits. Often static factories are preferable, so avoid the reflex to provide public constructors without first considering static factories.
总之,静态工厂方法和公有构造函数都有它们的作用,理解它们的相对优势是值得的。静态工厂经常是更合适的,因此要避免习惯性的提供公有构造函数而不首先考虑静态工厂。
总结:
1.静态工厂的优点:
与构造函数相比,静态工厂方法的第一个优势是它们有名字。
与构造函数相比,静态工厂方法的第二个优势是当调用静态工厂方法时不要求每次都创建一个新的对象。
与构造函数相比,静态工厂方法的第三个优势是它们能返回它们的返回类型的任意子类型的对象。
静态工厂方法的第四个优势是它们降低了创建参数化类型实例的冗长性。
2.静态工厂的缺点:
只提供静态工厂方法的主要缺点是没有公有或保护构造函数的类不能进行子类化。
静态工厂方法的第二个缺点是它们不能很容易的与其它静态方法进行区分。