Effective-Java-3rd (第三版第一条)

CHAPTER2 第二章

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

第一条:考虑静态工厂方法而不是构造函数

The traditional way for a class to allow a client to obtain an instance 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 boolean). This method translates a boolean primitive value into a Boolean object reference:

类允许客户端获取实例的传统方法是提供公共构造函数。还有一种技术应该成为每个程序员工具箱的一部分。类可以提供一个公共静态工厂方法,它只是一个返回类实例的静态方法。下面是布尔值的一个简单示例(布尔值的装箱原始类)。该方法将布尔类型的原始值转换为布尔对象引用:

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]. The static factory method described in this item has no direct equivalent in Design Patterns.

注意,静态工厂方法与Design Patterns [Gamma95]中的工厂方法模式不同。本项目中描述的静态工厂方法在设计模式中没有直接等效的方法。

A class can provide its clients with static factory methods instead of, or in addition to, public 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 added in Java 4.)

静态工厂方法的一个优点是,与构造函数不同,它们有名称。 如果构造函数的参数本身不能描述返回的对象,那么具有精心选择的名称的静态工厂更容易使用,写出的代码也更容易阅读。例如,构造函数 BigInteger(int, int, Random) ,返回一个可能是质数的BigInteger,它最好表示为一个名为 BigInteger. probableprime 的静态工厂方法。(这个方法是在Java 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 17) 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 illus- trates this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95]. It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create.

静态工厂方法的第二个优点是,与构造函数不同,它们不需要在每次调用时都创建一个新的对象。 这允许不可变类(第17条)使用预先构造的实例,或者在实例被构造时缓存它们,并反复地分发它们,以避免创建不必要的重复对象。 Boolean.valueof(boolean) 方法实现了这种技术: 它从不创建对象。这种技术类似于 Flyweight(享元模式)[Gamma95]。如果经常请求一样的对象,特别是创建它们的成本很高,那么它可以极大地提高性能。

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 value class (Item 17) to make the guarantee that no two equal instances exist: a.equals(b) if and only if a == b. This is the basis of the Flyweight pattern [Gamma95]. Enum types (Item 34) provide this guarantee.

静态工厂方法能够从重复调用中返回相同的对象,这使得类能够在任何时候严格控制存在哪些实例。这样做的类称为实例控制类。编写实例控制类有几个原因。实例控制允许一个类来保证它是一个单例(第3条)项或 noninstantiable (第4条)。同时,它允许一个不可变的值类(第17条)保证不存在两个相同的实例: a.equals(b) 。有且只有一个 a == b 。这是享元模式的基础[Gamma95]。枚举类型(第34项)提供此保证。

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 20), where interfaces provide natural return types for static factory methods.

这种灵活性的一个应用是API可以返回对象而不公开它们的类。以这种方式隐藏实现类会产生非常紧凑的API。这种技术适用于基于接口的框架(第20项),其中接口为静态工厂方法提供了自然的返回类型。

Prior to Java 8, interfaces couldn’t have static methods. By convention, static factory methods for an interface named Type were put in a noninstantiable companion class (Item 4) named Types. For example, the Java Collections Framework has forty-five utility implementations of its interfaces, providing unmodifiable collections, synchronized collections, and the like. Nearly all of these implemen- tations are exported via static factory methods in one noninstantiable class (java.util.Collections). The classes of the returned objects are all nonpublic.

在Java 8之前,接口不能有静态方法。按照惯例,一个名为Type的接口的静态工厂方法放在一个不可实例化的同伴类(Item 4)的命名类型中。例如,Java Collections框架有45个接口的实用工具实现,提供不可修改的集合、同步集合等。几乎所有这些实现都是通过一个非实例化类(java.util.Collections)中的静态工厂方法导出的。返回对象的类都是非公共的。

The Collections Framework API is much smaller than it would have been had it exported forty-five separate public classes, one for each convenience implemen- tation. It is not just the bulk of the API that is reduced but the conceptual weight: the number and difficulty of the concepts that programmers must master in order to use the API. The programmer knows that the returned object has precisely the API specified by its interface, so there is no need to read additional class docu- mentation for the implementation class. Furthermore, using such a static factory method requires the client to refer to the returned object by interface rather than implementation class, which is generally good practice (Item 64).

Collections框架API比导出45个独立的公共类(每个类用于方便的实现)要小得多。减少的不仅仅是API的体积,还有概念的权重: 程序员使用API必须掌握的概念的数量和难度。程序员知道返回的对象具有接口所指定的API,因此不需要为实现类读取额外的类文档。此外,使用这种静态工厂方法要求客户端通过接口引用返回的对象,而不是通过实现类引用,这通常是一种良好的实践(第64条)。

As of Java 8, the restriction that interfaces cannot contain static methods was eliminated, so there is typically little reason to provide a noninstantiable companion class for an interface. Many public static members that would have been at home in such a class should instead be put in the interface itself. Note, however, that it may still be necessary to put the bulk of the implementation code behind these static methods in a separate package-private class. This is because Java 8 requires all static members of an interface to be public. Java 9 allows private static methods, but static fields and static member classes are still required to be public.

从Java 8开始,接口不能包含静态方法的限制已经消除,因此通常没有理由为接口提供一个不可实例化的伙伴类。许多本来应该在此类中的公共静态成员应该放在接口本身中。但是,请注意,仍然有必要将这些静态方法背后的大部分实现代码放在单独的包私有类中。这是因为Java 8要求接口的所有静态成员都是公共的。Java 9允许私有静态方法,但是静态字段和静态成员类仍然需要是公共的。

A fourth advantage of static factories is that the class of the returned object can vary from call to call as a function of the input parameters. Any sub- type of the declared return type is permissible. The class of the returned object can also vary from release to release.

静态工厂的第四个优点是,作为输入参数的函数,返回对象的类可以随着调用的不同而变化。 声明的返回类型的任何子类型都是允许的。返回的对象的类在不同版本之间也可能有所不同。

The EnumSet class (Item 36) has no public constructors, only static factories. In the OpenJDK implementation, they return an instance of one of two subclasses, 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.

EnumSet 类(第36条)没有公共构造函数,只有静态工厂。在OpenJDK实现中,根据底层枚举类型的大小,它们返回两个子类之一的实例: 如果它有64个或更少的元素,就像大多数枚举类型所做的那样,静态工厂返回一个正则枚举集实例,该实例由一个 long 支持;如果枚举类型有65个或更多元素,则工厂返回一个由长数组支持的 JumboEnumSet 实例。

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 的某个子类。

A fifth advantage of static factories is that the class of the returned object need not exist when the class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, like the Java Database Connectivity API (JDBC). A service provider framework is a system in which providers implement a service, and the system makes the implementations available to clients, decoupling the clients from the implementations.

静态工厂的第五个优点是:在编写包含方法的类时,返回对象的类不需要存在。 这种灵活的静态工厂方法形成了服务提供者框架的基础,比如Java数据库连接 API (JDBC) 。服务提供者框架是这样一种系统: 提供者在其中实现服务,而该系统使客户端可以使用这些实现,从而将客户端与实现解耦。

There are three essential components in a service provider framework: a service interface, which represents an implementation; a provider registration API, which providers use to register implementations; and a service access API, which clients use to obtain instances of the service. The service access API may allow clients to specify criteria for choosing an implementation. In the absence of such criteria, the API returns an instance of a default implementation, or allows the client to cycle through all available implementations. The service access API is the flexible static factory that forms the basis of the service provider framework.

服务提供者框架中有三个基本组件: 服务接口,表示实现; 提供者注册API,提供者使用该API注册实现; 以及服务访问API,客户端使用该API获取服务的实例。服务访问API允许客户端指定选择实现的标准。在缺少这些条件的情况下,API返回默认实现的一个实例,或者允许客户端遍历所有可用的实现。服务访问API是灵活的静态工厂,它构成了服务提供者框架的基础。

An optional fourth component of a service provider framework is a service provider interface, which describes a factory object that produce instances of the service interface. In the absence of a service provider interface, implementations must be instantiated reflectively (Item 65). 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.

服务提供者框架的第四个可选组件是服务提供者接口,它描述生成服务接口实例的工厂对象。在缺少服务提供者接口的情况下,必须利用反射实例化实现(第65项)。在JDBC中,连接扮演服务接口 DriverManager 的角色。registerDriver 是提供者注册API, DriverManagergetConnection 是服务访问API, Driver 是服务提供者接口。

There are many variants of the service provider framework pattern. For example, the service access API can return a richer service interface to clients than the one furnished by providers. This is the Bridge pattern [Gamma95]. Dependency injection frameworks (Item 5) can be viewed as powerful service providers. Since Java 6, the platform includes a general-purpose service provider framework, java.util.ServiceLoader, so you needn’t, and generally shouldn’t, write your own (Item 59). JDBC doesn’t use ServiceLoader, as the former predates the latter.

服务提供者框架模式有许多变体。例如,服务访问API可以向客户返回比提供者提供的服务接口更丰富的服务接口。这是桥接模式[Gamma95]。依赖注入框架(第5项)可以看作是功能强大的服务提供者。自Java 6以来,该平台包括一个通用服务提供者框架 Java .util.ServiceLoader ,所以您不需要也不应该编写自己的(第59项)。JDBC不使用 ServiceLoader ,因为前者先于后者。

The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed. 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 because it encourages programmers to use composition instead of inheritance (Item 18), and is required for immutable types (Item 17).

仅提供静态工厂方法的主要限制是,没有公共或受保护构造函数的类不能被子类化。 例如,不可能在集合框架中子类化任何方便的实现类。这可能是因祸得福,因为它鼓励程序员使用组合而不是继承(第18条),并且对于不可变类型(第17条)也是必需的。

A second shortcoming of static factory methods is that they are hard for programmers to find. 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 problem by drawing attention to static factories in class or interface documentation and by adhering to common naming conventions. Here are some common names for static factory methods. This list is far from exhaustive:

静态工厂方法的第二个缺点是程序员很难找到它们。 在API文档中,它们不像构造函数那样突出,因此很难确定如何实例化一个提供静态工厂方法而不是构造函数的类。 Javadoc 工具可能有一天会引起对静态工厂方法的注意。同时,您可以通过关注类或接口文档中的静态工厂以及遵循公共命名约定来减少这个问题。下面是一些静态工厂方法的常见名称。这个清单只是部分:

  • from — A type-conversion method that takes a single parameter and returns a corresponding instance of this type, for example:

类型转换方法,它接受单个参数并返回该类型的相应实例,例如:

Date d = Date.from(instant);
  • of — An aggregation method that takes multiple parameters and returns an in- stance of this type that incorporates them, for example:

一个聚合方法,接受多个参数,并返回一个在这种类型的立场,合并他们,例如:

Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
  • valueOf — A more verbose alternative to from and of, for example:

一个更详细的替代方案,例如:

BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance or getInstance — Returns an instance that is described by its parameters (if any) but cannot be said to have the same value, for example:

返回一个实例,该实例由其参数(如果有)描述,但不能说具有相同的值,例如:

StackWalker luke = StackWalker.getInstance(options);
  • create or newInstance — Like instance or getInstance, except that the
    method guarantees that each call returns a new instance, for example:

例如instance或getInstance,除了方法确保每个调用返回一个新实例,例如:

Object newArray = Array.newInstance(classObject, arrayLen);
  • getType — Like getInstance, but used if the factory method is in a different
    class. Type is the type of object returned by the factory method, for example:

类似于getInstance,但如果工厂方法在其他地方,则使用类。Type是工厂方法返回的对象类型

FileStore fs = Files.getFileStore(path);
  • newType — Like newInstance, but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example:

与newInstance类似,但如果工厂方法在不同的类中,则使用该方法。Type是工厂方法返回的对象类型,例如:

BufferedReader br = Files.newBufferedReader(path);
  • type — A concise alternative to getType and newType, for example:

getType和newType的简洁替代方案,例如:

List<Complaint> litany = Collections.list(legacyLitany);

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.

总之,静态工厂方法和公共构造函数都有各自的用途,了解它们的相对优点是有好处的。通常静态工厂更可取,因此避免在不考虑静态工厂的情况下提供公共构造器的反射。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349