## Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
### Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
It is often appropriate to reuse a single object instead of creating a new functionally equivalent object each time it is needed. Reuse can be both faster and more stylish. An object can always be reused if it is immutable (Item 17).
復用單個對象通常是合適的,不必每次需要時都創建一個新的功能等效對象。復用可以更快、更流行。如果對象是不可變的,那么它總是可以被復用的([Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。
As an extreme example of what not to do, consider this statement:
作為一個不該做的極端例子,請考慮下面的語句:
```
String s = new String("bikini"); // DON'T DO THIS!
```
The statement creates a new String instance each time it is executed, and none of those object creations is necessary. The argument to the String constructor ("bikini") is itself a String instance, functionally identical to all of the objects created by the constructor. If this usage occurs in a loop or in a frequently invoked method, millions of String instances can be created needlessly.
該語句每次執行時都會創建一個新的 String 實例,而這些對象創建都不是必需的。String 構造函數的參數 `("bikini")` 本身就是一個 String 實例,在功能上與構造函數創建的所有對象相同。如果這種用法發生在循環或頻繁調用的方法中,創建大量 String 實例是不必要的。
The improved version is simply the following:
改進后的版本如下:
```
String s = "bikini";
```
This version uses a single String instance, rather than creating a new one each time it is executed. Furthermore, it is guaranteed that the object will be reused by any other code running in the same virtual machine that happens to contain the same string literal [JLS, 3.10.5].
這個版本使用單個 String 實例,而不是每次執行時都創建一個新的實例。此外,可以保證在同一虛擬機中運行的其他代碼都可以復用該對象,只要恰好包含相同的字符串字面量 [JLS, 3.10.5]。
You can often avoid creating unnecessary objects by using static factory methods (Item 1) in preference to constructors on immutable classes that provide both. For example, the factory method Boolean.valueOf(String) is preferable to the constructor Boolean(String), which was deprecated in Java 9. The constructor must create a new object each time it’s called, while the factory method is never required to do so and won’t in practice. In addition to reusing immutable objects, you can also reuse mutable objects if you know they won’t be modified.
你通常可以通過使用靜態工廠方法([Item-1](/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))來避免創建不必要的對象,而不是在提供這兩種方法的不可變類上使用構造函數。例如,工廠方法 `Boolean.valueOf(String)` 比構造函數 ~~Boolean(String)~~ 更可取,后者在 Java 9 中被棄用了。構造函數每次調用時都必須創建一個新對象,而工廠方法從來不需要這樣做,在實際應用中也不會這樣做。除了復用不可變對象之外,如果知道可變對象不會被修改,也可以復用它們。
Some object creations are much more expensive than others. If you’re going to need such an “expensive object” repeatedly, it may be advisable to cache it for reuse. Unfortunately, it’s not always obvious when you’re creating such an object. Suppose you want to write a method to determine whether a string is a valid Roman numeral. Here’s the easiest way to do this using a regular expression:
有些對象的創建的代價相比而言要昂貴得多。如果你需要重復地使用這樣一個「昂貴的對象」,那么最好將其緩存以供復用。不幸的是,當你創建這樣一個對象時,這一點并不總是很明顯。假設你要編寫一個方法來確定字符串是否為有效的羅馬數字。下面是使用正則表達式最簡單的方法:
```
// Performance can be greatly improved!
static boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}
```
The problem with this implementation is that it relies on the String.matches method. **While String.matches is the easiest way to check if a string matches a regular expression, it’s not suitable for repeated use in performance-critical situations.** The problem is that it internally creates a Pattern instance for the regular expression and uses it only once, after which it becomes eligible for garbage collection. Creating a Pattern instance is expensive because it requires compiling the regular expression into a finite state machine.
這個實現的問題是它依賴于 `String.matches` 方法。**雖然 String.matches 是檢查字符串是否與正則表達式匹配的最簡單方法,但它不適合在性能關鍵的情況下重復使用。** 問題在于,它在內部為正則表達式創建了一個 Pattern 實例,并且只使用一次,之后就進行垃圾收集了。創建一個 Pattern 實例是很昂貴的,因為它需要將正則表達式編譯成有限的狀態機。
To improve the performance, explicitly compile the regular expression into a Pattern instance (which is immutable) as part of class initialization, cache it,and reuse the same instance for every invocation of the isRomanNumeral method:
為了提高性能,將正則表達式顯式編譯為 Pattern 實例(它是不可變的),作為類初始化的一部分,緩存它,并在每次調用 isRomanNumeral 方法時復用同一個實例:
```
// Reusing expensive object for improved performance
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
}
```
The improved version of isRomanNumeral provides significant performance gains if invoked frequently. On my machine, the original version takes 1.1 μs on an 8-character input string, while the improved version takes 0.17 μs, which is 6.5 times faster. Not only is the performance improved, but arguably, so is clarity. Making a static final field for the otherwise invisible Pattern instance allows us to give it a name, which is far more readable than the regular expression itself.
如果頻繁調用 isRomanNumeral,改進版本將提供顯著的性能提升。在我的機器上,初始版本輸入 8 字符的字符串花費 1.1μs,而改進的版本需要 0.17μs,快 6.5 倍。不僅性能得到了改善,清晰度也得到了提高。為不可見的 Pattern 實例創建一個靜態終態字段允許我們為它命名,這比正則表達式本身更容易閱讀。
If the class containing the improved version of the isRomanNumeral method is initialized but the method is never invoked, the field ROMAN will be initialized needlessly. It would be possible to eliminate the initialization by lazily initializing the field (Item 83) the first time the isRomanNumeral method is invoked, but this is not recommended. As is often the case with lazy initialization, it would complicate the implementation with no measurable performance improvement (Item 67).
如果加載包含改進版 isRomanNumeral 方法的類時,該方法從未被調用過,那么初始化字段 ROMAN 是不必要的。因此,可以用延遲初始化字段([Item-83](/Chapter-11/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md))的方式在第一次調用 isRomanNumeral 方法時才初始化字段,而不是在類加載時初始化,但不建議這樣做。通常情況下,延遲初始化會使實現復雜化,而沒有明顯的性能改善([Item-67](/Chapter-9/Chapter-9-Item-67-Optimize-judiciously.md))。
**譯注:類加載通常指的是類的生命周期中加載、連接、初始化三個階段。當方法沒有在類加載過程中被使用時,可以不初始化與之相關的字段**
When an object is immutable, it is obvious it can be reused safely, but there are other situations where it is far less obvious, even counterintuitive. Consider the case of adapters [Gamma95], also known as views. An adapter is an object that delegates to a backing object, providing an alternative interface. Because an adapter has no state beyond that of its backing object, there’s no need to create more than one instance of a given adapter to a given object.
當一個對象是不可變的,很明顯,它可以安全地復用,但在其他情況下,它遠不那么明顯,甚至違反直覺。考慮適配器的情況 [Gamma95],也稱為視圖。適配器是委托給支持對象的對象,提供了一個替代接口。因為適配器的狀態不超過其支持對象的狀態,所以不需要為給定對象創建一個給定適配器的多個實例。
For example, the keySet method of the Map interface returns a Set view of the Map object, consisting of all the keys in the map. Naively, it would seem that every call to keySet would have to create a new Set instance, but every call to keySet on a given Map object may return the same Set instance. Although the returned Set instance is typically mutable, all of the returned objects are functionally identical: when one of the returned objects changes, so do all the others, because they’re all backed by the same Map instance. While it is largely harmless to create multiple instances of the keySet view object, it is unnecessary and has no benefits.
例如,Map 接口的 keySet 方法返回 Map 對象的 Set 視圖,其中包含 Map 中的所有鍵。天真的是,對 keySet 的每次調用都必須創建一個新的 Set 實例,但是對給定 Map 對象上的 keySet 的每次調用都可能返回相同的 Set 實例。雖然返回的 Set 實例通常是可變的,但所有返回的對象在功能上都是相同的:當返回的對象之一發生更改時,所有其他對象也會發生更改,因為它們都由相同的 Map 實例支持。雖然創建 keySet 視圖對象的多個實例基本上是無害的,但這是不必要的,也沒有好處。
Another way to create unnecessary objects is autoboxing, which allows the programmer to mix primitive and boxed primitive types, boxing and unboxing automatically as needed. **Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types.** There are subtle semantic distinctions and not-so-subtle performance differences (Item 61). Consider the following method, which calculates the sum of all the positive int values. To do this, the program has to use long arithmetic because an int is not big enough to hold the sum of all the positive int values:
另一種創建不必要對象的方法是自動裝箱,它允許程序員混合基本類型和包裝類型,根據需要自動裝箱和拆箱。**自動裝箱模糊了基本類型和包裝類型之間的區別,** 兩者有細微的語義差別和不明顯的性能差別([Item-61](/Chapter-9/Chapter-9-Item-61-Prefer-primitive-types-to-boxed-primitives.md))。考慮下面的方法,它計算所有正整數的和。為了做到這一點,程序必須使用 long,因為 int 值不夠大,不足以容納所有正整數值的和:
```
// Hideously slow! Can you spot the object creation?
private static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i;
return sum;
}
```
This program gets the right answer, but it is much slower than it should be,due to a one-character typographical error. The variable sum is declared as a Long instead of a long, which means that the program constructs about 231 unnecessary Long instances (roughly one for each time the long i is added to the Long sum). Changing the declaration of sum from Long to long reduces the runtime from 6.3 seconds to 0.59 seconds on my machine. The lesson is clear: **prefer primitives to boxed primitives, and watch out for unintentional autoboxing.**
這個程序得到了正確的答案,但是由于一個字符的印刷錯誤,它的速度比實際要慢得多。變量 sum 被聲明為 Long 而不是 long,這意味著程序將構造大約 231 個不必要的 Long 實例(大約每次將 Long i 添加到 Long sum 時都有一個實例)。將 sum 的聲明從 Long 更改為 long,機器上的運行時間將從 6.3 秒減少到 0.59 秒。教訓很清楚:**基本類型優于包裝類,還應提防意外的自動裝箱。**
This item should not be misconstrued to imply that object creation is expensive and should be avoided. On the contrary, the creation and reclamation of small objects whose constructors do little explicit work is cheap, especially on modern JVM implementations. Creating additional objects to enhance the clarity,simplicity, or power of a program is generally a good thing.
本條目不應該被曲解為是在暗示創建對象是成本昂貴的,應該避免。相反,創建和回收這些小對象的構造函數成本是很低廉的,尤其是在現代 JVM 實現上。創建額外的對象來增強程序的清晰性、簡單性或功能通常是件好事。
Conversely, avoiding object creation by maintaining your own object pool is a bad idea unless the objects in the pool are extremely heavyweight. The classic example of an object that does justify an object pool is a database connection.The cost of establishing the connection is sufficiently high that it makes sense to reuse these objects. Generally speaking, however, maintaining your own object pools clutters your code, increases memory footprint, and harms performance.Modern JVM implementations have highly optimized garbage collectors that easily outperform such object pools on lightweight objects.
相反,通過維護自己的對象池來避免創建對象不是一個好主意,除非池中的對象非常重量級。證明對象池是合理的對象的典型例子是數據庫連接。建立連接的成本非常高,因此復用這些對象是有意義的。然而,一般來說,維護自己的對象池會使代碼混亂,增加內存占用,并損害性能。現代 JVM 實現具有高度優化的垃圾收集器,在輕量級對象上很容易勝過這樣的對象池。
The counterpoint to this item is Item 50 on defensive copying. The present item says, “Don’t create a new object when you should reuse an existing one,”while Item 50 says, “Don’t reuse an existing object when you should create a new one.” Note that the penalty for reusing an object when defensive copying is called for is far greater than the penalty for needlessly creating a duplicate object. Failing to make defensive copies where required can lead to insidious bugs and security holes; creating objects unnecessarily merely affects style and performance.
與此項對應的條目是 [Item-50](/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md)(防御性復制)。當前項的描述是:「在應該復用現有對象時不要創建新對象」,而 Item 50 的描述則是:「在應該創建新對象時不要復用現有對象」。請注意,當需要進行防御性復制時,復用對象所受到的懲罰遠遠大于不必要地創建重復對象所受到的懲罰。在需要時不制作防御性副本可能導致潛在的 bug 和安全漏洞;而不必要地創建對象只會影響樣式和性能。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-2/Chapter-2-Introduction.md)**
- **Previous Item(上一條目):[Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)](/Chapter-2/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md)**
- **Next Item(下一條目):[Item 7: Eliminate obsolete object references(排除過時的對象引用)](/Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md)**
- Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
- Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)
- Item 2: Consider a builder when faced with many constructor parameters(在面對多個構造函數參數時,請考慮構建器)
- Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)
- Item 4: Enforce noninstantiability with a private constructor(用私有構造函數實施不可實例化)
- Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)
- Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
- Item 7: Eliminate obsolete object references(排除過時的對象引用)
- Item 8: Avoid finalizers and cleaners(避免使用終結器和清除器)
- Item 9: Prefer try with resources to try finally(使用 try-with-resources 優于 try-finally)
- Chapter 3. Methods Common to All Objects(對象的通用方法)
- Item 10: Obey the general contract when overriding equals(覆蓋 equals 方法時應遵守的約定)
- Item 11: Always override hashCode when you override equals(當覆蓋 equals 方法時,總要覆蓋 hashCode 方法)
- Item 12: Always override toString(始終覆蓋 toString 方法)
- Item 13: Override clone judiciously(明智地覆蓋 clone 方法)
- Item 14: Consider implementing Comparable(考慮實現 Comparable 接口)
- Chapter 4. Classes and Interfaces(類和接口)
- Item 15: Minimize the accessibility of classes and members(盡量減少類和成員的可訪問性)
- Item 16: In public classes use accessor methods not public fields(在公共類中,使用訪問器方法,而不是公共字段)
- Item 17: Minimize mutability(減少可變性)
- Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)
- Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用)
- Item 20: Prefer interfaces to abstract classes(接口優于抽象類)
- Item 21: Design interfaces for posterity(為后代設計接口)
- Item 22: Use interfaces only to define types(接口只用于定義類型)
- Item 23: Prefer class hierarchies to tagged classes(類層次結構優于帶標簽的類)
- Item 24: Favor static member classes over nonstatic(靜態成員類優于非靜態成員類)
- Item 25: Limit source files to a single top level class(源文件僅限有單個頂層類)
- Chapter 5. Generics(泛型)
- Item 26: Do not use raw types(不要使用原始類型)
- Item 27: Eliminate unchecked warnings(消除 unchecked 警告)
- Item 28: Prefer lists to arrays(list 優于數組)
- Item 29: Favor generic types(優先使用泛型)
- Item 30: Favor generic methods(優先使用泛型方法)
- Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
- Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)
- Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)
- Chapter 6. Enums and Annotations(枚舉和注解)
- Item 34: Use enums instead of int constants(用枚舉類型代替 int 常量)
- Item 35: Use instance fields instead of ordinals(使用實例字段替代序數)
- Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)
- Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
- Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)
- Item 39: Prefer annotations to naming patterns(注解優于命名模式)
- Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)
- Item 41: Use marker interfaces to define types(使用標記接口定義類型)
- Chapter 7. Lambdas and Streams(λ 表達式和流)
- Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
- Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)
- Item 44: Favor the use of standard functional interfaces(優先使用標準函數式接口)
- Item 45: Use streams judiciously(明智地使用流)
- Item 46: Prefer side effect free functions in streams(在流中使用無副作用的函數)
- Item 47: Prefer Collection to Stream as a return type(優先選擇 Collection 而不是流作為返回類型)
- Item 48: Use caution when making streams parallel(謹慎使用并行流)
- Chapter 8. Methods(方法)
- Item 49: Check parameters for validity(檢查參數的有效性)
- Item 50: Make defensive copies when needed(在需要時制作防御性副本)
- Item 51: Design method signatures carefully(仔細設計方法簽名)
- Item 52: Use overloading judiciously(明智地使用重載)
- Item 53: Use varargs judiciously(明智地使用可變參數)
- Item 54: Return empty collections or arrays, not nulls(返回空集合或數組,而不是 null)
- Item 55: Return optionals judiciously(明智地的返回 Optional)
- Item 56: Write doc comments for all exposed API elements(為所有公開的 API 元素編寫文檔注釋)
- Chapter 9. General Programming(通用程序設計)
- Item 57: Minimize the scope of local variables(將局部變量的作用域最小化)
- Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)
- Item 59: Know and use the libraries(了解并使用庫)
- Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)
- Item 61: Prefer primitive types to boxed primitives(基本數據類型優于包裝類)
- Item 62: Avoid strings where other types are more appropriate(其他類型更合適時應避免使用字符串)
- Item 63: Beware the performance of string concatenation(當心字符串連接引起的性能問題)
- Item 64: Refer to objects by their interfaces(通過接口引用對象)
- Item 65: Prefer interfaces to reflection(接口優于反射)
- Item 66: Use native methods judiciously(明智地使用本地方法)
- Item 67: Optimize judiciously(明智地進行優化)
- Item 68: Adhere to generally accepted naming conventions(遵守被廣泛認可的命名約定)
- Chapter 10. Exceptions(異常)
- Item 69: Use exceptions only for exceptional conditions(僅在確有異常條件下使用異常)
- Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors(對可恢復情況使用 checked 異常,對編程錯誤使用運行時異常)
- Item 71: Avoid unnecessary use of checked exceptions(避免不必要地使用 checked 異常)
- Item 72: Favor the use of standard exceptions(鼓勵復用標準異常)
- Item 73: Throw exceptions appropriate to the abstraction(拋出能用抽象解釋的異常)
- Item 74: Document all exceptions thrown by each method(為每個方法記錄會拋出的所有異常)
- Item 75: Include failure capture information in detail messages(異常詳細消息中應包含捕獲失敗的信息)
- Item 76: Strive for failure atomicity(盡力保證故障原子性)
- Item 77: Don’t ignore exceptions(不要忽略異常)
- Chapter 11. Concurrency(并發)
- Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)
- Item 79: Avoid excessive synchronization(避免過度同步)
- Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)
- Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
- Item 82: Document thread safety(文檔應包含線程安全屬性)
- Item 83: Use lazy initialization judiciously(明智地使用延遲初始化)
- Item 84: Don’t depend on the thread scheduler(不要依賴線程調度器)
- Chapter 12. Serialization(序列化)
- Item 85: Prefer alternatives to Java serialization(優先選擇 Java 序列化的替代方案)
- Item 86: Implement Serializable with great caution(非常謹慎地實現 Serializable)
- Item 87: Consider using a custom serialized form(考慮使用自定義序列化形式)
- Item 88: Write readObject methods defensively(防御性地編寫 readObject 方法)
- Item 89: For instance control, prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)
- Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例)