Tips
書中的源代碼地址:https://github.com/jbloch/effective-java-3e-source-code
注意,書中的有些代碼裏方法是基於Java 9 API中的,因此JDK 最好下載 JDK 9以上的版本。java
假設想要生成0到某個上界之間的隨機整數。對於這個常見的任務,許多程序員會編寫一個相似這樣的方法:git
// Common but deeply flawed! static Random rnd = new Random(); static int random(int n) { return Math.abs(rnd.nextInt()) % n; }
這個方法可能看起來不錯,但它有三個缺陷。 首先,若是n是一個比較小的2的乘方,則隨機數的序列將在至關短的時間段後開始重複。 第二個缺陷是,若是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); }
若是random方法正常工做,程序將打印接近50萬的數字,但若是運行它,你會發現它打印的數字接近666,666。random方法生成的三分之二數字落在其範圍的上半部分!github
random方法的第三個缺陷是,在極少數狀況下,它可能會災難性地失敗,返回超出指定範圍以外的數字。 這是由於該方法嘗試經過調用Math.abs
將rnd.nextInt()
返回的值映射到非負整數。 若是nextInt()
返回Integer.MIN_VALUE
,則Math.abs
也會返回Integer.MIN_VALUE
,假設n不是2的乘方,取模運算符(%)將返回負數。 這幾乎確定會致使程序失敗,而且可能難以重現。web
要編寫一個糾正這些缺陷的random方法的版本,你必須知道關於僞隨機數生成器,數論和二進制補碼算法的知識。幸運的是,你沒必要這樣作 —— 它已經爲你完成了,就是Random.nextInt(int)
方法。你沒必要關心它如何完成其工做的細節(,若是您很好奇,能夠研究文檔或源代碼)。一位具備算法背景的高級工程師花費了大量時間來設計,實現和測試這種方法,而後向該領域的幾位專家展現,以確保其正確性。而後,這個類庫通過了beta測試,發佈,並被數百萬程序員普遍使用了近二十年。該方法還沒有發現任何缺陷,但若是發現了缺陷,將在下一個版本中修復。經過使用標準類庫,能夠利用編寫類庫專家的知識以及在前人使用它的經驗。算法
從Java 7開始,就不該再使用Random了。 對於大多數用途,選擇的隨機數生成器如今是ThreadLocalRandom
。 它產生更高質量的隨機數,並且速度很是快。 在個人機器上,它比Random快3.6倍。 對於fork-join池和並行流的應用,請使用SplittableRandoms
。編程
使用這些類庫的第二個好處是,沒必要浪費時間爲那些與你的工做關聯不大的問題上,而去編寫專門的解決方案。若是像大多數程序員同樣,那麼寧願將時間花在應用程序上,而不是底層內容上。api
使用標準類庫的第三個優勢是,它們的性能會隨着時間的推移而不斷提升,而你無需付出任何努力。 由於許多人使用它們而且由於它們被用於行業標準基準測試,因此提供這些類庫的組織有強烈的動力使它們運行得更快。 多年來,許多Java平臺類庫都通過重寫,有時甚至是重複編寫,從而顯着提高性能。多線程
使用類庫的第四個優勢是它們傾向於隨着時間的推移不斷增長功能。 若是某個類庫遺失了某些東西,開發人員社區就會知道它,而且可能會在後續版本中添加缺乏的功能。併發
使用標準庫的最後一個好處是,能夠將代碼放在主流中。這樣的代碼更容易被開發人員閱讀、維護和重用。
鑑於全部這些優勢,使用類庫設施優先於專門實現彷佛是合乎邏輯的,但許多程序員並不這樣作。爲何不呢? 也許他們不知道類庫工具設施的存在。 在每一個主要版本中,都會向類庫中添加許多特性,瞭解這些新增特性是很是值得的。每次有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); } }
這些類庫太大了,以致於沒法學習全部文檔[Java9-api],但每一個程序員都應該熟悉java.lang
,java.util
和java.io
及其子包的基礎知識。 能夠根據須要獲取其餘類庫的知識。 總結類庫設施超出了本條目的範圍,這些設施多年來已經發展得很是龐大。
幾個類庫特別值得一提。 集合Collection框架和流Stream類庫(條目4——-48)應該是每一個程序員的基本工具包的一部分,java.util.concurrent中
的併發實用程序的也應如此。 該軟件既包含了用於簡化多線程編程任務的高級實用程序,還包括偏底層的原語,以容許專家編寫本身的高級併發抽象。 條目 80和81會討論java.util.concurrent
的高級部分。
有時,類庫設施可能沒法知足你的需求。需求越專門化,發生這種狀況的可能性就越大。雖然第一個衝動應該是使用這些類庫,可是若是已經瞭解了它們在某些領域提供的功能,而這些功能不能知足你的需求,那麼可使用另外一種實現。任何有限的類庫集所提供的功能老是存在漏洞。若是你在Java平臺庫中找不到你須要的東西,你的下一個選擇應該是尋找高質量的第三方庫,好比谷歌的優秀的開源Guava類庫[Guava]。若是沒法在任何適當的類庫中找到所需的功能,可能別無選擇,你只能本身實現了。
總而言之,不要從新發明輪子。 若是須要作一些彷佛應該至關常見的事情,那麼類庫中可能已經有了一個能夠知足你需求的工具。 若是有,請使用它; 若是不知道,請檢查。 通常來講,類庫代碼可能比您本身編寫的代碼更好,而且可能會隨着時間的推移而改進。 這並不反映你做爲程序員的能力。 規模經濟決定了類庫代碼獲得的關注遠遠超過大多數開發人員能夠承擔的相同的功能。