文章目录
- Effective第三版
- 第2章 创建和销毁对象
- 前言
- 考虑静态工厂方法而不是构造函数
Effective第三版
第2章 创建和销毁对象
前言
大家好,这里是 Rocky 编程日记 ,喜欢后端架构及中间件源码,目前正在阅读 effective-java 书籍。同时也把自己学习该书时的笔记,代码分享出来,供大家学习交流,如若笔记中有不对的地方,那一定是当时我的理解还不够,希望你能及时提出。如果对于该笔记存在很多疑惑,可以先去翻阅《Java核心技术》也欢迎和我交流讨论,最后也感谢您的阅读,点赞,关注,收藏~
前人述备矣,我只是知识的搬运工,effective 书籍源码均在开源项目 java-diary 中的 code-effective-third 模块中
源代码仓库地址: https://gitee.com/Rocky-BCRJ/java-diary.git
考虑静态工厂方法而不是构造函数
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:
类允许客户端获取实例的传统方法是提供公共构造器。还有一种技术应该是每个程序猿的工具箱的一部分。一个类可以提供一个公共静态工厂方法,它仅仅是一个返回类实例的静态方法。下面是布尔(基本类型 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]. The static factory method described in this item has no direct equivalent in 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 Java4.)
静态工厂方法的一个优点是,它们是有名称的,而构造函数的名称都是一样的。如果构造函数的参数本身不能描述返回的对象,那么使用一个精心命名的静态工厂更容易使用,并且生成的客户端代码更容易阅读。例如BigInteger
的一个构造函数:BigInteger(int, int, Random)
,这个构造函数返回一个BigInteger
有可能是一个质数,使用一个精心命名的静态工厂方法会更容易描述该方法返回的 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.
静态工厂方法的第二个优点是,与构造函数不同,它们不需要在每次被调用时创建一个新对象。这允许不可变类(第 17 项)使用预先构造的实例,或者在构建时缓存实例,并重复分发它们,以避免创建不必要的重复对象。
The Boolean.valueOf(boolean) method illustrates 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.
Boolean.valueOf(boolean)
方法使用了这种方式:它从不创建对象。这种技术类似于享元模式[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 项)或非实例化的(第 4 项),并且它允许一个不可变的值类(第 17 项)来保证没有两个相等的实例存在:当且仅当a==b
成立时,a.equals(b)
返回true
,这是享元模式 [Gamma95] 的基础,枚举类型就提供了这种保证。
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 变得非常简洁。这种技术适用于基于接口的框架(interface-based frameworks,见第 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 implementations 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 的接口的静态工厂方法被放置在一个不可实例化的名为 Types 的配套类(noninstantiable companion class)(第 4 项)中。例如,Java Collections Framework 有 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 implementation. 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 documentation 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 Framework 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 subtype 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 aJumboEnumSet instance, backed by a long array.
在EnumSet
类(第 36 项)中没有公有的构造方法,只有静态工厂方法。在 OpenJDK 实现中的,它们返回两个子类之一的实例,具体取决于基础枚举类型的大小:如果它有 64 个或更少的元素,就像大多数枚举类型所做的那样,静态工厂返回一个RegularEnumSet
实例, 它由单个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
的第三或第四个实现。客户即不知道也不关心他们从工厂中获取的对象的类型,他们只关心它是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.
静态工厂方法的第五个优点是,返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在。这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础,例如 JDBC(Java 数据库连接,Java Database Connectivity)API。服务提供者框架是提供者实现服务的系统,系统使实现可用于客户端,将客户端与实现分离【微服务】。
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 是灵活的静态工厂,它构成了服务提供者框架的基础。
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,Connection 扮演服务接口的一部分,DriverManager.registerDriver 是提供者注册 API,DriverManager.getConnection 是服务访问 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).
静态工厂方法的主要限制在于,类如果不含公有的或者受保护的构造器,就不能被子类化。例如:不可能将 Collections Framework 中的任何方便的实现类子类化。但是这也许会因祸得福,因为它鼓励程序猿使用组合,而不是继承(第 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:
from:一种类型转换方法,它接受单个参数并返回此类型的相应实例,
例如:
Date d = Date.from(instant);
-
of—An aggregation method that takes multiple parameters and returns an instance of this type that incorporates them, for
example:of:一种聚合方法,它接受多个参数并返回包含它们的此类型的实例,
例如:Set faceCards = EnumSet.of(JACK, QUEEN, KING);
-
valueOf—A more verbose alternative to from and of, for example:
valueOf:一个更详细的替代方案,
例如:
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:
instance or getInstance:返回由其参数(如果有)描述的实例,但不能说它具有相同的值,
例如:
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:
create or newInstance:与 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:
getType:与 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:
newType:与 newInstance 类似,是在工厂方法位于不同的类中时使用它。Type 指的是工厂方法返回的对象类型,例如:
BufferedReader br = Files.newBufferedReader(path);
-
type—A concise alternative to getType and newType, for example:
type:获取 Type 和 new Type 一个简明替代的方法,
例如:
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.
总之,静态工厂方法和公共构造函数都有它们的用途,理解它们的相对优点是值得的。通常静态工厂是优选的,不要在第一反应就是使用构造函数,应当先考虑使用静态工厂方法。