《Effective Java》

經典重讀——亞馬遜鏈接


筆記鏈接


導圖:

Effective Java overview.png

筆記文本:

Effective Java
1 第2章 創建和銷毀對象
1.1 1. 考慮用靜態工廠方法代替構造器
1.1.1 優點
1.1.1.1 優勢一:有名稱
1.1.1.2 優勢二:不必在每次調用它們的時候都創建一個新對象
1.1.1.3 優勢三:可以返回原返回類型的任何子類型的對象
1.1.1.4 優勢四:在創建參數化類型實例時,它們使代碼變得更簡潔
1.1.2 缺點
1.1.2.1 類如果不含有公有的或者受保護的構造器,就不能被子類化
1.1.2.2 它們與其他靜態方法實際上沒有任何區別
1.2 2. 遇到多個構造器參數時要考慮用構建器
1.2.1 Builder
1.2.2 與構造器相比的略微優勢在于:builder可以有多個可變參數
1.2.3 雖然創建builder的開銷在實踐中可能不那么明顯,但是在某些十分注重性能的情況下,可能就成問題
1.2.4 builder比重疊構造器更冗長,因此只在很多參數時才使用
1.3 3. 用私有構造器或者枚舉類型強化Singleton屬性
1.3.1 單元素的枚舉類型已經成為實現Singleton的最佳方法
1.4 4. 通過私有構造器強化不可實例化的能力
1.4.1 弊端:使得其不能被子類化
1.5 5. 避免創建不必要的對象
1.5.1 對于同時提供了靜態工廠方法和構造器的不可變類,通常可以使用靜態工廠方法而不是構造器,以避免創建不必要的對象
1.5.2 除了重用不可變的對象之外,也可以重用那些已知不會被修改的可變對象
1.5.3 要優先使用基本類型而不是裝箱基本類型,要當心無意識的自動裝箱
1.5.4 自己維護對象池只在對象非常重量級時才顯得必要,比如數據庫連接池,線程池
1.6 6. 消除過期的對象引用
1.6.1 一旦對象引用已經過期,只需清空這些引用即可
1.6.2 緩存導致的內存泄露
1.6.2.1 一種是確認在緩存之外存在對某幾個項的鍵的引用,該項就有意義 ——可以用WeakHashMap代表緩存
1.6.2.2 針對“緩存項生命周期是否有意義”難以確認的情況 ——緩存應該時不時地清除掉沒用的項。這項清除工作可以由一個后臺線程來完成,或者也可以在給緩存添加新條目時順便清理(LinkedHashMap類利用它的removeEldestEntry方法可以很容易實現)
1.6.2.3 第三種引發來源是:監聽器和其他回調。 確保回調立即被當做垃圾回收的最佳方法是只保存它們的弱引用 weak reference
1.7 7. 避免使用finalizer方法
1.7.1 通常是不可預測的
1.7.1.1 一個對象從不可用開始,到它的終結方法被執行,這段時間是任意長的
1.7.2 使用它有一個非常嚴重的性能損失
1.7.3 顯示的終止方法通常與try-finally結合使用,以確保及時終止
1.7.4 用途:充當安全網、本地對等體
1.7.5 結論:不應該依賴終結方法來更新重要的持久狀態
1.7.6 結論:除非是作為安全網,或者是為了終止非關鍵的本地資源,否則不要使用
2 第3章 對所有對象都通用的方法
2.1 8. 覆蓋equals時請遵守通用約定
2.1.1 建議:能不覆蓋就不要覆蓋
2.1.2 不用覆蓋的情形
2.1.2.1 類的每個實例本質上都是唯一的
2.1.2.2 不關心類是否提供了“邏輯相等”
2.1.2.3 超類已經覆蓋了equals,從超類繼承過來的行為對于子類也是合適的
2.1.2.4 類是私有的或是包級私有的,可以確定它的equals方法永遠不會被調用
2.1.3 覆蓋的約定
2.1.3.1 自反性
2.1.3.2 對稱性
2.1.3.3 傳遞性
2.1.3.4 一致性
2.1.3.5 非空性
2.1.3.5.1 對于任何非null的引用值,x.equals(null)必須返回false
2.1.4 訣竅
2.1.4.1 使用== 檢查參數是否為這個對象的引用
2.1.4.2 使用instanceof檢查 參數是否為正確的類型
2.1.4.3 把參數轉換為正確的類型
2.1.4.4 對于該類中的每個關鍵域,檢查參數中的域是否與該對象中對應的域相匹配
2.1.4.5 是否滿足:對稱、傳遞、一致
2.1.4.6 覆蓋equals時總要覆蓋hashCode
2.1.4.7 不要企圖讓equals方法過于智能
2.1.4.8 不要將equals聲明中的Object對象替換為其他類型
2.2 9. 覆蓋 equals時總要覆蓋 hashCode
2.2.1 最佳實踐:
2.2.1.1 @Override public int hashCode(){ int result = 17; result = 31 * result + areaCode; result = 31 * result + prefix; result = 31 * result + lineNumber; }
2.3 10. 總要覆蓋 toString
2.3.1 toString方法應該返回對象中包含的所有值得關注的信息
2.4 11. 謹慎地覆蓋clone
2.4.1 通則:永遠不要讓客戶去做任何類庫能夠替客戶完成的事情
2.4.2 最好提供某些其他途徑來代替對象拷貝,或者干脆不提供這樣的功能
2.4.3 另一個實現對象拷貝的好方法是提供一個拷貝構造器或拷貝工廠
2.4.4 由于它有這么多缺點,有些專家級的程序員干脆從來不去覆蓋clone方法,也從來不去調用它,除非拷貝數組
2.5 12. 考慮實現comparable接口
2.5.1 compareTo不但允許進行簡單的等同性比較,而且允許執行順序比較
2.5.2 如果你在編寫一個值類,它具有非常明顯的內在排序關系,比如按字母順序、按數值順序或者按年代順序,那你就應該堅決考慮實現這個接口
2.5.3 如果一個類有多個關鍵域,那么你必須從最關鍵的域開始,逐步進行到所有的重要域
3 第4章 類和接口
3.1 13. 使類和成員的可訪問性最小化
3.1.1 信息隱藏 或 封裝
3.1.2 盡可能地使每個類或者成員不被外界訪問
3.1.3 有一條規則限制了降低方法的可訪問性能力:如果方法覆蓋了超類中的一個方法,子類中的訪問級別就不允許低于超類中的訪問級別
3.1.4 注意:長度非零的數組總是可變的,所以,類具有公有的靜態final數組域或者返回這種域的訪問方法總是會出錯
3.2 14. 在公有類中使用訪問方法而非公有域
3.2.1 如果類可以在它所在的包的外部進行訪問,就提供訪問方法
3.2.2 如果類是包級私有的,或者是私有的嵌套類,直接暴露它的數據域并沒有本質錯誤
3.2.3 Java類庫的反面典型:Point和Dimension,暴露dimension類的內部數據造成了嚴重的性能問題,而且這個問題依然存在
3.3 15. 使可變性最小化
3.3.1 理由:比可變類更加易于設計、實現和使用。它們不容易出錯,且更加安全
3.3.2 使類成為不可變需要遵循五條規則
3.3.2.1 1 不要提供任何會修改對象狀態的方法
3.3.2.2 2 保證類不會被擴展
3.3.2.3 使所有的域都是final的
3.3.2.4 使所有的域都成為私有的
3.3.2.5 確保對于任何可變組件的互斥訪問
3.3.3 不可變對象的優勢
3.3.3.1 不可變對象本質上是線程安全的,它們不要求同步
3.3.3.2 不僅可以共享不可變對象,甚至可以共享它們的內部信息
3.3.3.3 不可變對象為其他對象提供了大量的構件
3.3.3.4 不可變對象真正唯一的缺點是,對于每個不同的值都需要一個單獨的對象
3.3.4 使類成為不可變的方法 (絕不允許自身被子類化)
3.3.4.1 1 使類成為final
3.3.4.2 2 讓類的所有構造器都變成私有的或者包級私有的,并添加公有的靜態工廠來替代公有的構造器
3.3.4.3 靜態工廠方法除了允許多個實現類的靈活性之外,這種方法還使得有可能通過改善靜態工廠的對象緩存能力,在后續的發行版中改進該類的性能
3.3.4.4 還有許多其他優勢,比如,提供一種其他功能的 構造器 ,只需添加第二個靜態工廠,并且工廠的名字清楚地表明它的功能
3.3.5 如果你選擇讓自己的不可變類實現Serializable接口,并且包含一個或多個指向可變對象的域,就必須提供一個顯式的readObject或者readResolve方法
3.3.6 除非有很好的理由要讓類成為一個可變的類,否則就應該是不可變的
3.3.7 如果類不能被做成不可變的,仍然應該盡可能地限制它的可變性。降低對象可存在的狀態數,可以更容易地分析該對象的行為,同時降低出錯的可能性
3.4 16. 復合優先于繼承
3.4.1 繼承是實現代碼重用的有力手段,但它并非永遠是完成這項工作的最佳工具
3.4.2 與方法調用不同的是,繼承打破了封裝性
3.4.3 新類中的每個實例方法都可以調用被包含的現有類實例中對應的方法,并返回它的結果,這被稱為“轉發”,新類中的方法被稱為轉發方法
3.4.4 使用復合的類也被稱為包裝類,這也正是“裝飾模式”
3.4.5 包裝類不適合用在回調框架中。 編寫轉發方法倒是有點瑣碎,但是只需要給每個接口編寫一次構造器,轉發類則可以通過包含接口的包替你提供
3.4.6 只有當子類真正是超類的子類型時,才適合用繼承
3.4.7 如果在適合于使用復合的地方使用繼承,則會不必要地暴露實現細節。這樣得到的API會把你限制在原始的實現上,永遠限定了類的性能
3.5 17. 要么為繼承而設計,并提供文檔說明,要么就禁止繼承
3.5.1 說明可覆蓋方法的自用性
3.5.2 好的文檔應該描述一個給定的方法做了什么,而不是如何做到的
3.5.3 對于為了繼承而設計的類,唯一的測試方法就是編寫子類
3.5.4 構造器決不能調用可被覆蓋的方法
3.5.5 為了繼承而設計的類,對于這個類會有一些實質性的限制
3.5.6 對于那些并非為了安全地進行子類化而設計和編寫文檔的類,要禁止子類化
3.6 18. 接口優于抽象類
3.6.1 現有的類可以很容易被更新,以實現新的接口
3.6.2 接口是定義mixin(混合類型)的理想選擇
3.6.3 接口允許我們構造非層次結構的類型框架
3.6.4 接口使得安全地增強類的功能成為可能
3.6.5 通過對你導出的每個重要接口都提供一個抽象的骨架實現類,把接口和抽象類的優點結合起來
3.6.6 抽象類與接口相比有一個明顯優勢:抽象類的演變比接口的演變要容易得多。如果在后續發行版中,你希望在抽象類中增加新的方法,始終可以增加具體方法,它包含合理的默認實現。然后,該抽象類的所有實現都將提供這個新方法。對于接口,這樣做是行不通的
3.6.7 設計公有接口要非常謹慎。接口一旦被公開發行,并且已被廣泛實現,再想改變這個接口幾乎是不可能的
3.7 19. 接口只用于定義類型
3.7.1 有一種接口被稱為 常量接口。這種接口不包含任何方法,它只包含靜態final域,每個域都導出一個常量。使用這些常量的類實現這個接口,以避免用類名來修飾常量名。
3.7.2 應該將這種量導出
3.8 20. 類層次優于標簽類
3.8.1 缺點
3.8.1.1 充斥著樣板代碼,包括枚舉聲明、標簽域以及條件語句
3.8.1.2 標簽類過于冗長、容易出錯,并且效率低下
3.9 21. 用函數對象表示策略
3.9.1 比較器函數代表一種為元素排序的策略
3.9.2 我們在設計具體的策略類時,還需要定義一個策略接口
3.10 22. 優先考慮靜態成員類
3.10.1 嵌套類是指被定義在另一個類的內部的類。嵌套類有四種
3.10.1.1 靜態成員類
3.10.1.2 非靜態成員類
3.10.1.3 匿名類
3.10.1.4 局部類
3.10.2 如果聲明成員類不要求訪問外圍實例,就要始終把static修飾符放在它的聲明中
4 第5章 泛型
4.1 23. 請不要在新代碼中使用原生態類型
4.2 24. 消除非受檢警告
4.2.1 要盡可能消除每一個非受檢警告
4.2.2 如果無法消除警告,同時可以證明引起警告的代碼是類型安全的,(只有在這種情況下才)可以用一個@SuppressWarning("uncheck")注解來禁止這條警告
4.3 25. 列表優先于數組
4.3.1 數組與泛型相比,有兩個重要不同點

    4.3.1.1 1 數組是協變的
    4.3.1.2 2 數組是具體化的

4.4 26. 優先考慮泛型
4.4.1 編寫自己的泛型會比較困難,但是值得花些時間去學習如何編寫
4.5 27. 優先考慮泛型方法
4.5.1 泛型方法就像泛型一樣,使用起來比要求客戶端轉換輸入參數并返回值的方法來得更加安全,也更加容易。
4.5.2 就像泛型一樣,你應該確保新方法可以不用轉換就能使用,這通常意味著要將它們泛型化
4.6 28. 利用有限制通配符來提升API的靈活性
4.6.1 <? Extends E>
4.6.2 <? super E>
4.6.3 PECS 表示: producer-extends, consumer-super
4.6.4 所有的comparable和comparator都是消費者
4.7 29. 優先考慮類型安全的異構容器
5 第6章 枚舉和注解
5.1 第30條 用enum代替int常量
5.1.1 因為沒有可訪問的構造器,枚舉類型是真正的final
5.1.2 枚舉是實例受控的
5.1.3 它們是單例的泛型化,本質上是單元素的枚舉
5.1.4 你可以增加或重新排列枚舉類型中的常量,而無需重新編譯它的客戶端代碼,因為導出常量的域在枚舉類型和它的客戶端之間提供了一個隔離層:常量值并沒有被編譯到客戶端代碼中,而是在int枚舉模式之中
5.1.5 最終,可以通過調用toString方法將枚舉轉化成可打印的字符串
5.1.6 除了完善了int枚舉模式不足之外,枚舉類型還允許添加任意的方法和域,并實現任意接口
5.1.7 將不同的行為與每個枚舉常量關聯起來:在枚舉類型中聲明一個抽象的方法
5.1.7.1 pubic enum Operation{ PLUS{double apply(double x, double y){return x + y;}}, MINUS{double apply(double x, double y){return x - y;}}, TIMES{double apply(double x, double y){return x * y;}}, DIVIDE{double apply(double x, double y){return x / y;}}; abstract double apply(double x, double y); }
5.1.7.2 如果給第二版添加新常量,你就不可能會忘記提供apply方法
5.1.8 特定于常量的方法實現可以與特定于常量的數據結合起來
5.1.8.1 pubic enum Operation{ PLUS("+") {double apply(double x, double y){return x + y;}}, MINUS("-") {double apply(double x, double y){return x - y;}}, TIMES("*") {double apply(double x, double y){return x * y;}}, DIVIDE("/") {double apply(double x, double y){return x / y;}}; private final String symbol; Operation(String symbol) {this.symbol = symbol;} @Override public String toString(){ return this.sybol;} abstract double apply(double x, double y); }
5.1.9 枚舉類型有一個自動產生的valueOf(String )方法,它將常量的名字轉變成常量本身
5.1.10 策略枚舉
5.1.10.1 嵌套策略枚舉之后,枚舉中就不需要switch語句或者特定于常量的方法實現了。雖然這種模式沒有switch語句那么簡潔,但更加安全,也更加靈活
5.2 第31條 用實例域代替序數
5.2.1 永遠不要根據枚舉的序數導出與它關聯的值,而是要將它保存在一個實例域中
5.2.2 ordinal()——大多數程序員都不需要這個方法,它是設計成用于像EnumSet和EnumMap這種基于枚舉的通用數據結構的
5.3 第32條 用EnumSet代替位域
5.3.1 枚舉類型要用于集合中,沒有理由用位域來表示它們
5.4 第33條 用EnumMap代替序數索引
5.4.1 使用EnumMap,運行速度可以與使用序數的程序相媲美,沒有不安全的轉換,不必手工標注這些索引的輸出;計算數組索引時也不可能出錯
5.4.2 之所以能與通過序數索引的數組相媲美,是因為EnumMap在內部使用了這種數組
5.4.3 最好不要用序數來索引數組,而要使用EnumMap
5.5 第34條 用接口模擬可伸縮的枚舉
5.5.1 雖然無法編寫可擴展的枚舉類型,卻可以通過編寫接口以及實現該接口的基礎枚舉類型,對它進行模擬
5.6 第35條 注解優先于命名模式
5.6.1 示例
5.6.1.1 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test{ }
5.6.1.2 這種注解稱為元注解
5.7 第36條 堅持使用Override注解
5.7.1 能夠避免疏漏的 重載
5.8 第37條 用標記接口定義類型
5.8.1 定義:沒有包含方法聲明的接口,而只是指明一個類實現了具有某種屬性的接口
6 第7章 方法
6.1 第38條 檢查參數的有效性
6.2 第39條 必要時進行保護性拷貝
6.2.1 假設類的客戶端會盡其所能來破壞這個類的約束條件,因此你必須保護性地設計程序
6.2.2 為了保護類的內部信息免受直接訪問修改的攻擊,對于構造器的每個可變參數進行保護性拷貝是必要的
6.2.3 對與參數類型可以被不可信任方子類化的參數,請不要使用clone方法進行保護性拷貝
6.2.4 雖然替換構造器就可以成功地避免上述攻擊,但是改變Period實例仍然是有可能的,因為它的訪問方法提供了對其可變內部成員的訪問能力 為了防御這種攻擊,只需要修改訪問方法,使它返回可變內部域的保護性拷貝即可
6.3 第40條 謹慎設計方法簽名
6.3.1 謹慎選擇方法的名稱
6.3.2 不要過于追求提供便利的方法
6.3.2.1 每個方法都應該盡其所能。
6.3.2.2 方法太多會使類難以學習、使用、文檔化、測試和維護
6.3.2.3 只有當一項操作被經常使用時,才考慮為它提供快捷方法,如果不能確定,還是不提供為好
6.3.3 避免過長的參數列表
6.3.3.1 相同類型的長參數列表格外有害
6.3.3.2 例如,List接口提供了subList方法,返回子列表的視圖(view)
6.3.3.3 解決方法
6.3.3.3.1 1 分解為多個方法
6.3.3.3.2 2 創建輔助類
6.3.3.3.3 3 采用builder模式
6.3.3.4 對參數類型,要優先使用接口而不是類
6.3.3.5 對于boolean參數,要優先使用兩個元素的枚舉類型
6.4 第41條 慎用重載
6.4.1 對于重載方法的選擇是靜態的,而對于被覆蓋的方法的選擇則是動態的
6.4.2 安全而保守的策略是,永遠不要導出兩個具有相同參數數目的重載方法
6.5 第42條 慎用可變參數
6.5.1 EnumSet類對它的靜態工廠使用這種方法,最大限度地減少創建枚舉集合的成本。枚舉集合為位域提供在性能方面有競爭力的替代方法
6.6 第43條 返回零長度的數組或者集合,而不是null
6.6.1 對于不返回任何元素的調用,每次都返回同一個零長度數組是有可能的,因為零長度數組是不可變的
6.7 第44條 為所有導出的API元素編寫文檔注釋
6.7.1 為了正確地編寫API文檔,必須在每個被導出的 類、接口、構造器、方法和域聲明之前增加一個文檔注釋
6.7.2 方法的文檔注釋應該簡潔地描述出它和客戶端之間的約定
6.7.2.1 precondition
6.7.2.2 postcondition
6.7.2.3 side effect
6.7.2.4 thread safety
6.7.3 可以使用HTML標簽
6.7.4 使用{@code}標簽
6.7.5 {@literal}
7 第8章 通用程序設計
7.1 第45條 將局部變量的作用域最小化
7.1.1 幾乎每個局部變量的聲明都應該包含一個初始化表達式
7.1.2 使用for循環,可以大大降低 “剪切-粘貼”錯誤
7.1.3 使用for循環與使用while相比還有另外一個優勢:更簡短,從而增強可讀性
7.2 第46條 for-each循環優先于傳統的for循環
7.2.1 for-each循環不僅讓你遍歷集合和數組,還讓你遍歷任何實現Iterable接口的對象
7.2.2 如果你在編寫的類型表示的是一組元素,即使你選擇不讓它實現Collection,也要讓它實現Iterable。這樣可以允許用戶利用for-each循環遍歷你的類型
7.2.3 有三種常見情況無法使用for-each循環
7.2.3.1 過濾
7.2.3.2 轉換
7.2.3.3 平行迭代
7.3 第47條 了解和使用類庫
7.4 第48條 如果需要精確答案,請避免使用float和double
7.4.1 二進制浮點運算
7.4.2 float和double不適合貨幣計算
7.4.3 使用BigDecimal、int或者long進行
7.5 第49條 基本類型優先于裝箱基本類型
7.5.1 裝箱類型的合理用處
7.5.1.1 作為集合中的元素、鍵和值
7.5.1.2 在參數化類型中,必須使用裝箱基本類型作為類型參數
7.5.1.3 在反射調用時,必須使用裝箱基本類型
7.6 第50條 如果其他類型更合適,則盡量避免使用字符串
7.6.1 字符串不適合代替其他的值類型
7.6.2 字符串不適合代替枚舉類型
7.6.3 字符串不適合代替聚集類型
7.6.4 字符串不適合代替能力表
7.7 第51條 當心字符串連接的性能
7.7.1 使用StringBuilder替代
7.8 第52條 通過接口引用對象
7.8.1 如果你養成了用接口作為類型的習慣,你的程序將更加靈活
7.8.2 如果沒有合適的接口存在,完全可以用類而不是接口來引用對象
7.9 第53條 接口優先于反射機制
7.9.1 反射
7.9.1.1 喪失了編譯時類型檢查的好處
7.9.1.2 執行反射訪問所需要的代碼非常笨拙和冗長
7.9.1.3 性能損失
7.10 第54條 謹慎地使用本地方法
7.10.1 使用本地方法提升性能的做法不值得提倡
7.11 第55條 謹慎地進行優化
7.11.1 優化的弊大于利
7.11.2 要努力編寫好的程序而不是快的程序
7.11.3 努力避免那些限制性能的設計決策
7.11.4 要考慮API設計決策的性能后果
7.11.5 為了獲得好的性能而對API進行包裝,這是一種非常不好的想法
7.12 第56條 遵守普遍接受的命名規則
8 第9章 異常
8.1 第57條 只針對異常的情況才使用異常
8.1.1 基于異常的模式比標準模式要慢得多
8.2 第58條 對可恢復的情況使用受檢異常,對編程錯誤使用運行時異常
8.2.1 受檢異常
8.2.2 用運行時異常來表明編程錯誤
8.3 第59條 避免不必要地使用受檢異常
8.4 第60條 優先使用標準異常
8.5 第61條 拋出與抽象相對應的異常
8.5.1 更高層的實現應該捕獲低層的異常,同時拋出可以按照高層抽象進行解釋的異常
8.5.2 異常鏈
8.6 第62條 每個方法拋出的異常都要有文檔
8.6.1 單獨聲明受檢異常,使用Javadoc的@throws標記
8.6.2 不要在非受檢異常上使用@throws
8.7 第63條 在細節消息中包含能捕獲失敗的信息
8.7.1 為了捕獲失敗,異常的細節信息應該包含所有“對該異常有貢獻”的參數和域的值
8.8 第64條 努力使失敗保持原子性
8.8.1 失敗的方法調用應該使對象保持在被調用之前的狀態
8.9 第65條 不要忽略異常
8.9.1 空的catch塊達不到應有的目的
8.9.2 至少包含一條忽略的說明
9 第10章 并發
9.1 第66條 同步訪問共享的可變數據
9.1.1 Java語言規范保證讀或者寫一個變量是原子性的
9.1.2 volatile修飾符不執行互斥訪問,但它可以保證任一線程在讀取該域時都將看到最近剛被寫入的值
9.1.3 使用volatile時,務必要小心,++操作并不是原子性的
9.1.4 AtomicInt、AtomicLong
9.1.4.1 getAndIncrement
9.1.5 多線程共享可變數據時,每個讀或者寫數據的線程都必行執行同步
9.2 第67條 避免過度同步
9.3 第68條 executor和task優先于線程
9.3.1 Executor FrameWork中有一個可以替代Timer的東西,即ScheduleThreadPoolExecutor 雖然就Timer更加容易,但executor更靈活。Timer對于長期運行任務時會影響到定時準確性。 如果Timer唯一的線程拋出未被捕獲異常,Timer就會停止執行,而executor支持多個線程,并且優雅地從拋出的未受檢異常的任務中恢復
9.4 第69條 并發工具優先于wait和notify
9.4.1 既然正確地使用wait和notify比較困難,就應該用更高級的并發工具來代替
9.4.2 concurrent工具分三類:Executor Framework、并發集合 以及 同步器
9.4.2.1 并發集合為標準的集合接口(如List、Queue和Map)
9.4.2.1.1 ConcurrentHashMap出了提供卓越的并發性之外,速度也非常快
9.4.2.1.2 優先使用ConcurrentHashMap而不是Hashtable或者Collections.synchronizedMap
9.4.2.2 有些集合接口已經通過阻塞操作進行了擴展,他們會一直等待可以成功執行為止
9.4.2.2.1 如BlockingQueue
9.4.2.2.2 不出所料,大多數ExecutorService實現(包括ThreadPoolExecutor)都使用BlockingQueue
9.4.2.3 同步器 是一些使線程能夠等待另一個線程的對象,允許它們協調動作
9.4.2.3.1 最常用的是CountDownLatch和Semaphore
9.4.2.3.2 對于間歇式定時,應該使用System.nanoTime,更加精準
9.4.2.3.3 傳遞給Timer方法的executor必須允許創建至少與指定并發級別一樣多的線程,否則這個測試就永遠不會結束,這就是線程饑餓死鎖
9.4.3 使用wait和notify就像用“并發匯編語言”進行編程一樣
9.4.4 維護時:一般情況下你應該優先使用notifyAll,而不是notify
9.5 第70條 線程安全性的文檔化
9.6 第71條 慎用延遲初始化
9.6.1 除非絕對必要,否則就不要這么做
9.6.2 在大多數情況下,正常的初始化要優先于延遲初始化
9.6.3 如果處于性能考慮需要對實例域使用延遲初始化,就使用雙重檢查模式
9.7 第72條 不要依賴于線程調度器
9.7.1 任何依賴于線程調度器來達到正確性或者性能要求的程序,很有可能都是不可移植的
9.7.2 不要依賴Thread.yield或者線程優先級。這些設施僅僅對調度器作些按時
9.7.3 線程優先級可以用來提高一個已經能夠正常工作的程序的服務質量,但永遠不應該用來“修正”一個原本不能正常工作的程序
9.8 第73條 避免使用線程組
10 第11章 序列化
10.1 第74條 謹慎實現Serializable接口
10.1.1 代價
10.1.1.1 降低了改變這個類的實現的靈活性
10.1.1.2 增加了出現Bug和安全漏洞的可能性
10.1.1.3 相關的測試負擔增加
10.2 第75條 考慮使用自定義的序列化形式
10.3 第76條 保護性地編寫readObject方法
10.4 第77條 對于實例控制,枚舉類型優先于readResolve
10.5 第78條 考慮用序列化代理代替序列化實例

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容