## Chapter 9. General Programming(通用程序設計)
### Item 59: Know and use the libraries(了解并使用庫)
Suppose you want to generate random integers between zero and some upper bound. Faced with this common task, many programmers would write a little method that looks something like this:
假設你想要生成 0 到某個上界之間的隨機整數。面對這個常見任務,許多程序員會編寫一個類似這樣的小方法:
```
// Common but deeply flawed!
static Random rnd = new Random();
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
```
This method may look good, but it has three flaws. The first is that if n is a small power of two, the sequence of random numbers will repeat itself after a fairly short period. The second flaw is that if n is not a power of two, some numbers will, on average, be returned more frequently than others. If n is large, this effect can be quite pronounced. This is powerfully demonstrated by the following program, which generates a million random numbers in a carefully chosen range and then prints out how many of the numbers fell in the lower half of the range:
這個方法看起來不錯,但它有三個缺點。首先,如果 n 是小的平方數,隨機數序列會在相當短的時間內重復。第二個缺陷是,如果 n 不是 2 的冪,那么平均而言,一些數字將比其他數字更頻繁地返回。如果 n 很大,這種效果會很明顯。下面的程序有力地證明了這一點,它在一個精心選擇的范圍內生成 100 萬個隨機數,然后打印出有多少個數字落在范圍的下半部分:
```
public static void main(String[] args) {
int n = 2 * (Integer.MAX_VALUE / 3);
int low = 0;
for (int i = 0; i < 1000000; i++)
if (random(n) < n/2)
low++;
System.out.println(low);
}
```
If the random method worked properly, the program would print a number close to half a million, but if you run it, you’ll find that it prints a number close to 666,666. Two-thirds of the numbers generated by the random method fall in the lower half of its range!
如果 random 方法工作正常,程序將輸出一個接近 50 萬的數字,但是如果運行它,你將發現它輸出一個接近 666666 的數字。隨機方法生成的數字中有三分之二落在其范圍的下半部分!
The third flaw in the random method is that it can, on rare occasions, fail catastrophically, returning a number outside the specified range. This is so because the method attempts to map the value returned by rnd.nextInt() to a non-negative int by calling Math.abs. If nextInt() returns Integer.MIN_VALUE, Math.abs will also return Integer.MIN_VALUE, and the remainder operator (%) will return a negative number, assuming n is not a power of two. This will almost certainly cause your program to fail, and the failure may be difficult to reproduce.
random 方法的第三個缺陷是,在極少數情況下會返回超出指定范圍的數字,這是災難性的結果。這是因為該方法試圖通過調用 `Math.abs` 將 `rnd.nextInt()` 返回的值映射到非負整數。如果 `nextInt()` 返回整數。`Integer.MIN_VALUE`、`Math.abs` 也將返回整數。假設 n 不是 2 的冪,那么 `Integer.MIN_VALUE` 和求模運算符 `(%)` 將返回一個負數。幾乎肯定的是,這會導致你的程序失敗,并且這種失敗可能難以重現。
To write a version of the random method that corrects these flaws, you’d have to know a fair amount about pseudorandom number generators, number theory, and two’s complement arithmetic. Luckily, you don’t have to do this— it’s been done for you. It’s called Random.nextInt(int). You needn’t concern yourself with the details of how it does its job (although you can study the documentation or the source code if you’re curious). A senior engineer with a background in algorithms spent a good deal of time designing, implementing, and testing this method and then showed it to several experts in the field to make sure it was right. Then the library was beta tested, released, and used extensively by millions of programmers for almost two decades. No flaws have yet been found in the method, but if a flaw were to be discovered, it would be fixed in the next release. **By using a standard library, you take advantage of the knowledge of the experts who wrote it and the experience of those who used it before you.**
要編寫一個 random 方法來糾正這些缺陷,你必須對偽隨機數生成器、數論和 2 的補碼算法有一定的了解。幸運的是,你不必這樣做(這是為你而做的成果)。它被稱為 `Random.nextInt(int)`。你不必關心它如何工作的(盡管如果你感興趣,可以研究文檔或源代碼)。一位具有算法背景的高級工程師花了大量時間設計、實現和測試這種方法,然后將其展示給該領域的幾位專家,以確保它是正確的。然后,這個庫經過 beta 測試、發布,并被數百萬程序員廣泛使用了近 20 年。該方法還沒有發現任何缺陷,但是如果發現了缺陷,將在下一個版本中進行修復。**通過使用標準庫,你可以利用編寫它的專家的知識和以前使用它的人的經驗。**
As of Java 7, you should no longer use Random. For most uses, **the random number generator of choice is now ThreadLocalRandom.** It produces higher quality random numbers, and it’s very fast. On my machine, it is 3.6 times faster than Random. For fork join pools and parallel streams, use SplittableRandom.
從 Java 7 開始,就不應該再使用 Random。在大多數情況下,**選擇的隨機數生成器現在是 ThreadLocalRandom。** 它能產生更高質量的隨機數,而且速度非常快。在我的機器上,它比 Random 快 3.6 倍。對于 fork 連接池和并行流,使用 SplittableRandom。
A second advantage of using the libraries is that you don’t have to waste your time writing ad hoc solutions to problems that are only marginally related to your work. If you are like most programmers, you’d rather spend your time working on your application than on the underlying plumbing.
使用這些庫的第二個好處是,你不必浪費時間為那些與你的工作無關的問題編寫專門的解決方案。如果你像大多數程序員一樣,那么你寧愿將時間花在應用程序上,而不是底層管道上。
A third advantage of using standard libraries is that their performance tends to improve over time, with no effort on your part. Because many people use them and because they’re used in industry-standard benchmarks, the organizations that supply these libraries have a strong incentive to make them run faster. Many of the Java platform libraries have been rewritten over the years, sometimes repeatedly, resulting in dramatic performance improvements. A fourth advantage of using libraries is that they tend to gain functionality over time. If a library is missing something, the developer community will make it known, and the missing functionality may get added in a subsequent release.
使用標準庫的第三個優點是,隨著時間的推移,它們的性能會不斷提高,而你無需付出任何努力。由于許多人使用它們,而且它們是在行業標準基準中使用的,所以提供這些庫的組織有很強的動機使它們運行得更快。多年來,許多 Java 平臺庫都被重新編寫過,有時甚至是反復編寫,從而帶來了顯著的性能改進。使用庫的第四個好處是,隨著時間的推移,它們往往會獲得新功能。如果一個庫丟失了一些東西,開發人員社區會將其公布于眾,并且丟失的功能可能會在后續版本中添加。
A final advantage of using the standard libraries is that you place your code in the mainstream. Such code is more easily readable, maintainable, and reusable by the multitude of developers.
使用標準庫的最后一個好處是,可以將代碼放在主干中。這樣的代碼更容易被開發人員閱讀、維護和復用。
Given all these advantages, it seems only logical to use library facilities in preference to ad hoc implementations, yet many programmers don’t. Why not? Perhaps they don’t know the library facilities exist. **Numerous features are added to the libraries in every major release, and it pays to keep abreast of these additions.** Each time there is a major release of the Java platform, a web page is published describing its new features. These pages are well worth reading [Java8-feat, Java9-feat]. To reinforce this point, suppose you wanted to write a program to print the contents of a URL specified on the command line (which is roughly what the Linux curl command does). Prior to Java 9, this code was a bit tedious, but in Java 9 the transferTo method was added to InputStream. Here is a complete program to perform this task using this new method:
考慮到所有這些優點,使用庫工具而不選擇專門的實現似乎是合乎邏輯的,但許多程序員并不這樣做。為什么不呢?也許他們不知道庫的存在。**在每個主要版本中,都會向庫中添加許多特性,了解這些新增特性是值得的。** 每次發布 Java 平臺的主要版本時,都會發布一個描述其新特性的 web 頁面。這些頁面非常值得一讀 [Java8-feat, Java9-feat]。為了強調這一點,假設你想編寫一個程序來打印命令行中指定的 URL 的內容(這大致是 Linux curl 命令所做的)。在 Java 9 之前,這段代碼有點乏味,但是在 Java 9 中,transferTo 方法被添加到 InputStream 中。這是一個使用這個新方法執行這項任務的完整程序:
```
// Printing the contents of a URL with transferTo, added in Java 9
public static void main(String[] args) throws IOException {
try (InputStream in = new URL(args[0]).openStream()) {
in.transferTo(System.out);
}
}
```
The libraries are too big to study all the documentation [Java9-api], but **every programmer should be familiar with the basics of java.lang, java.util, and java.io, and their subpackages.** Knowledge of other libraries can be acquired on an as-needed basis. It is beyond the scope of this item to summarize the facilities in the libraries, which have grown immense over the years.
庫太大,無法學習所有文檔 [Java9-api],但是 **每個程序員都應該熟悉 `java.lang`、`java.util` 和 `java.io` 的基礎知識及其子包。** 其他庫的知識可以根據需要獲得。概述庫中的工具超出了本項目的范圍,這些工具多年來已經發展得非常龐大。
Several libraries bear special mention. The collections framework and the streams library (Items 45–48) should be part of every programmer’s basic toolkit, as should parts of the concurrency utilities in java.util.concurrent. This package contains both high-level utilities to simplify the task of multithreaded programming and low-level primitives to allow experts to write their own higher-level concurrent abstractions. The highlevel parts of java.util.concurrent are discussed in Items 80 and 81.
有幾個圖書館值得一提。collections 框架和 streams 庫(可參看 Item 45-48)應該是每個程序員的基本工具包的一部分,`java.util.concurrent` 中的并發實用程序也應該是其中的一部分。這個包既包含高級的并發工具來簡化多線程的編程任務,還包含低級別的并發基本類型,允許專家們自己編寫更高級的并發抽象。`java.util.concurrent` 的高級部分,在 [Item-80](/Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md) 和 [Item-81](/Chapter-11/Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md) 中討論。
Occasionally, a library facility can fail to meet your needs. The more specialized your needs, the more likely this is to happen. While your first impulse should be to use the libraries, if you’ve looked at what they have to offer in some area and it doesn’t meet your needs, then use an alternate implementation. There will always be holes in the functionality provided by any finite set of libraries. If you can’t find what you need in Java platform libraries, your next choice should be to look in high-quality third-party libraries, such as Google’s excellent, open source Guava library [Guava]. If you can’t find the functionality that you need in any appropriate library, you may have no choice but to implement it yourself.
有時,類庫工具可能無法滿足你的需求。你的需求越專門化,發生這種情況的可能性就越大。雖然你的第一個思路應該是使用這些庫,但是如果你已經了解了它們在某些領域提供的功能,而這些功能不能滿足你的需求,那么可以使用另一種實現。任何有限的庫集所提供的功能總是存在漏洞。如果你在 Java 平臺庫中找不到你需要的東西,你的下一個選擇應該是尋找高質量的第三方庫,比如谷歌的優秀的開源 Guava 庫 [Guava]。如果你無法在任何適當的庫中找到所需的功能,你可能別無選擇,只能自己實現它。
To summarize, don’t reinvent the wheel. If you need to do something that seems like it should be reasonably common, there may already be a facility in the libraries that does what you want. If there is, use it; if you don’t know, check. Generally speaking, library code is likely to be better than code that you’d write yourself and is likely to improve over time. This is no reflection on your abilities as a programmer. Economies of scale dictate that library code receives far more attention than most developers could afford to devote to the same functionality.
總而言之,不要白費力氣重新發明輪子。如果你需要做一些看起來相當常見的事情,那么庫中可能已經有一個工具可以做你想做的事情。如果有,使用它;如果你不知道,檢查一下。一般來說,庫代碼可能比你自己編寫的代碼更好,并且隨著時間的推移可能會得到改進。這并不反映你作為一個程序員的能力。規模經濟決定了庫代碼得到的關注要遠遠超過大多數開發人員所能承擔的相同功能。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-9/Chapter-9-Introduction.md)**
- **Previous Item(上一條目):[Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)](/Chapter-9/Chapter-9-Item-58-Prefer-for-each-loops-to-traditional-for-loops.md)**
- **Next Item(下一條目):[Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)](/Chapter-9/Chapter-9-Item-60-Avoid-float-and-double-if-exact-answers-are-required.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(考慮以序列化代理代替序列化實例)