《Effective Java 第二版》讀書筆記

fancybox發表於2018-12-12

想成為更優秀,更高效程式設計師,請閱讀此書。總計78個條目,每個對應一個規則。

第二章 建立和銷燬物件

  一,考慮用靜態工廠方法代替構造器

  二, 遇到多個構造器引數時要考慮用builder模式

      /**
    * 1.直接傳參閱讀性差,2.JavaBean多執行緒下存在安全問題,3.builder適用於大於5個引數並且引數是可選得情況 建造者(builder) 模式
    * @author fancy
    */
    public class Person implements Serializable {
      private String name;
      private String age;
      private String sex;
      private String country;
      private String edc;
      public Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.country = builder.country;
        this.edc = builder.edc;
      }
      public static class Builder {
        private String name;
        private String age;
        private String sex;
        private String country;
        private String edc;
        public Builder setName(String name) {
          this.name = name;
          return this;
        }
        public Builder setAge(String age) {
          this.age = age;
          return this;
        }
        public Builder setSex(String sex) {
          this.sex = sex;
          return this;
        }
        public Builder setCountry(String country) {
          this.country = country;
          return this;
        }
        public Builder setEdc(String edc) {
          this.edc = edc;
          return this;
        }
        public Person build() {
          return new Person(this);
        }
      }
      public static void main(String[] args) {
         Person person = new Person.Builder().setAge("20").setName("fancy").build();
      }
    }

  三,用私有構造器或者列舉型別強化Singleton屬性

  四,通過私有構造器強化不可例項化的能力

     工具類例項對它沒任何意義,新增私有構造器是有必要的。

      public class StringUtil {

      private StringUtil () {}

    }

  五,避免建立不必要的物件

     能重用物件的時候不要建立新物件。

     優先使用基本型別,儘量避免自動裝箱。

  六,消除過期的物件引用

     長生命週期的物件持有短生命週期物件的引用就很可能發生記憶體洩漏。

     引用物件已經過期,清空引用=null。

  七,比用使用終結方法

     終結方法(finalizer)通常是不可以預測的,也是很危險的。終結方法的缺點在於不能保證會被及時地執行。

     final:java中的關鍵字,修飾符。

     A).如果一個類被宣告為final,就意味著它不能再派生出新的子類,不能作為父類被繼承。因此,一個類不能同時被宣告為abstract抽象類的和final的類。

     B).如果將變數或者方法宣告為final,可以保證它們在使用中不被改變.

         1)被宣告為final的變數必須在宣告時給定初值,而在以後的引用中只能讀取,不可修改。

         2)被宣告final的方法只能使用,不能過載。

     finally:java的一種異常處理機制。

     finally是對Java異常處理模型的最佳補充。finally結構使程式碼總會執行,而不管無異常發生。使用finally可以維護物件的內部狀態,並可以清理非記憶體資源。特別是在關閉

資料庫連線這方面,如果程式設計師把資料庫連線的close()方法放到finally中,就會大大降低程式出錯的機率。

     finalize:Java中的一個方法名。

     Java技術使用finalize()方法在垃圾收集器將物件從記憶體中清除出去前,做必要的清理工作。這個方法是由垃圾收集器在確定這個物件沒被引用時對這個物件呼叫的。它是

在Object類中定義的,因此所的類都繼承了它。子類覆蓋finalize()方法以整理系統資源或者執行其他清理工作。finalize()方法是在垃圾收集器刪除物件之前對這個物件呼叫的。

第三章 對於所有物件都通用的方法

  Object是一個具體得類,設計它主要為了擴充套件。

  八,覆蓋equals時請遵守通用約定

     類具有自己獨特的“邏輯相等”概念,這個時候需要覆蓋equals方法,

  九,覆蓋equals時總要覆蓋hashcode

     相等的(equals)的物件必須具有相等的hash code。不相等的兩個物件,hash code有可能相等。

  十,始終要覆蓋toString

     返回物件中所有值得關注得資訊

  十一,謹慎地覆蓋clone()方法

     除非拷貝陣列,否則沒必要覆蓋

  十二,考慮實現Comparable介面

    類具有非常明顯得內在排序關係,比如按照字母排序、按照數值排序,應該考慮使用這個介面。

第四章 類和介面

  十三, 使類和成員的可訪問性最小化

     設計良好得模組會隱藏所有得實現細節

     儘可能降低可訪問性。

  十四,在公有類中使用訪問方法而非公有域

     公有類永遠都不應該暴露可變得域。

  十五,使可變性最小化

     除非有很好的理由,堅決不要每個get方法編寫set方法,儘量每個域都是final的。

  十六,複合優先於繼承

     private final Set<E> s;// 增加一個私有的成員變數,繼承違背了封裝原則,子類父類存在子型別關係時才用繼承。

     這樣做等同於新建一個類,而這個類的全部作用就是給原來繼承的父類的所有已知方法進行代理。

     代理方法是死的,不會隨原父類方法的擴充而改變,他只是呼叫原父類的方法,所以,當原父類新增方法時這個代理的類是不知道也不會受到影響的。

     這樣做也是有缺陷的:

     雖然這樣不會因為父類的擴充套件和子類發生衝突,但同樣的父類擴充套件的好處也不會被子類自動繼承,這時需要手動去更新代理類,設想一下,在一個比較複雜的系統中,所

有繼承關係都使用這種複合關係,那麼任何非葉級的類增加方法都要在該類的複合類中增加代理方法,程式碼維護工作量會增加,而且父類增加方法時無法立即知道新方法與子類可能

存在的衝突,而導致了重複的工作。

     一般父類擴充套件方法與子類發生衝突是因為子類在擴充套件方法時沒有從物件分類上進行充分的考慮導致在錯誤的分類範圍定義錯誤的行為,是可以從設計上就避免的問題。

  十七,要麼為繼承而設計並提供文件說明,要麼就禁止繼承

  十八,介面優於抽象類

     抽象類允許包含某些方法的實現;介面不允許。

     為實現抽象類定義的型別,類必須成為抽象類的一個子類,Java只允許單繼承;可以implement多個介面

  十九,介面只用於定義型別

     介面不應該用來定義常量。

  二十,類層次優於標籤類、

     標籤類:香蕉蘋果放到一個標籤類中,冗長

     類層次:水果父類,香蕉蘋果分別繼承

  二十一,用函式物件表示策略

     函式指標的主要用途就是實現策略(Strategy)模式。Java中宣告一個介面表示策略,並且為每個具體策略宣告一個實現了該介面的類。例:Arrays.sort(array, comparator);

  二十二,優先考慮靜態成員類

     巢狀類有四種:靜態成員類,非靜態成員類,匿名類,區域性類。

第五章 泛型

  二十三,不要在新程式碼中使用原生態型別

     原生態型別可能導致執行時異常,保留是為了相容性

     泛型<E>,<Object>引數化型別,<?>無限制萬用字元型別

  二十四,消除非受檢警告

     儘可能消除每一個非受檢警告,執行時可能丟擲ClassCastException。

     無法消除使用註釋,@SuppressWarnings("unchecked")

  二十五,List優先於陣列

     String型別放入int型別陣列,執行時才會丟擲異常

  二十六,優先考慮泛型

  二十七,優先考慮泛型方法

  二十八,利用有限制萬用字元來提升API的靈活性

  二十九,優先考慮型別安全的異構容器

第六章 列舉和註解

  三十,用enum代替int常量

     enum type:由一組固定的常量組成合法值的型別。

     功能強大,int數字翻譯成字串

  三十一,用例項域代替序數

     除了設計基於EnumSet,EnumMap外資料結構外基本用不到。

  三十二,用EnumSet代替位域

  三十三,用EnumMap代替序數索引

  三十四,用介面模擬可伸縮的列舉

  三十五,註解優先於命名模式

  三十六,堅持用Override註解

     覆蓋父類方法上使用,編輯器可以替你防止大量錯誤。

  三十七,用標記介面定義型別

     標記介面(marker interface)是沒有包含方法宣告的介面。例:Serializable

第七章 方法

  三十八,檢查引數有效性

     在方法體得開頭處檢查引數。比如物件是否為null。

  三十九,必要時進行保護性拷貝

    public Date getBrithday() {  

        return (Date) this.birthday.clone();  // 我造個新的例項扔給你,你隨便折騰吧,影響不到我  ,防止使用者改變。

    } 

  四十,謹慎設計方法簽名

     謹慎選擇方法名、不要過於追求提供便利的方法、避免過長引數列表

  四十一,慎用過載

     儘量不要出現有兩個相同引數的過載方法。

  四十二,慎用可變引數

  四十三,返回零長度的陣列或者集合而不是null

     簡化呼叫者非null判斷工作

  四十四,為所有匯出的API元素編寫文件註釋

第八章 通用程式設計

  四十五,將區域性變數的作用域最小化

     第一次使用區域性變數的地方宣告

  四十六,for-each迴圈優先於傳統的for迴圈

     簡潔、預防bug、效能優勢

     以下三種情況無法使用for-each:1.過濾,2.轉換,3.平衡迭代

  四十七,瞭解和使用類庫

     不要重新發明輪子

  四十八,如果需要精確的答案,請避免使用float和double

     如果數值範圍不超過9位十進位制用int

     如果數值範圍不超過18位十進位制用long

     如果數值範圍超過18位十進位制用BigDecimal

  四十九,基本型別優先於裝箱型別

     基本型別更加簡單,也更加快速。

  五十,如果其他型別更適合,則避免使用字串

  五十一,當心字串連結的效能

     認識String類

       1)String類是final類

       2)String類其實是通過char陣列來儲存字串的。

       3)無論是sub操作、concat還是replace操作都不是在原有的字串上進行的,而是重新生成了一個新的字串物件。

     StringBuffer類的成員方法前面多了一個關鍵字:synchronized,在多執行緒訪問時起到安全保護作用的

     StringBuilder不是執行緒安全的。

     String、StringBuilder、StringBuffer三者的執行效率:StringBuilder > StringBuffer > String

  五十二,優先使用介面而非類來引用物件

     List list = new ArrayList();

  五十三,介面優先於反射機制

     反射適用於部分物件例項化,不適合訪問物件。

  五十四,謹慎地使用本地方法

     本地方法指本地程式設計語言(C,Python)來編寫的特殊方法。

     必須使用需進行全面測試。

  五十五,謹慎地進行優化

     沒有好的優化方法不要優化。

  五十六,遵守普遍接受的命名慣例

第九章 異常

  五十七,只針對異常的情況才使用異常

     不要用於普通的控制流。

  五十八,對可恢復的情況使用受檢異常,對程式設計錯誤使用執行時異常

  五十九,避免不必要地使用受檢的異常

  六十,優先使用標準的異常

     常用異常。

  六十一,丟擲與抽象相對應的異常

  六十二,每個方法丟擲的異常都要有文件

  六十三,在細節訊息中包含能捕獲失敗的資訊

  六十四,努力使失敗保持原子性

     失敗的方法呼叫應該使物件保持在被呼叫之前的狀態

       1.設計不可變的物件,重新初始化常量

       2.操作之前檢查有效性

       3.編寫恢復程式碼

  六十五,不要忽略異常

第十章 併發

  六十六,同步訪問共享的可變資料

     多個執行緒共享可變資料得時候,每個讀或寫操作得執行緒都必須是同步的。

  六十七,避免過度同步

     同步區域內做盡可能少的工作。避免效能降低和其他不確定性影響。

  六十八,executor和task優先於執行緒

     《Java併發程式設計實踐》

  六十九,併發工具優先於wait和notify

     java.util.conconcurrent提供了更高階的語言,沒有必要使用wait和notify。

  七十,執行緒安全性的文件化

     執行緒安全註解或說明。

  七十一,慎用延遲初始化

  七十二,不要依賴於執行緒排程器

  七十三,避免使用執行緒組

     過時了,可忽略。

第十一章 序列化

  定義:將一個物件編碼成位元組流,稱作將該物件序列化(serializing),相反的過程稱為反序列化(deserializing)

  好處:物件被序列化後,它的編碼就可以從一臺正在執行的虛擬機器被傳遞到另一臺虛擬機器上,或者被儲存到磁碟上,供反序列化使用。

  應用:序列化技術為遠端通訊提供了標準的小路級(wire-level)物件表示法,也為JavaBeans元件結構提供了標準的持久化格式。

  七十四,謹慎地實現Serializable介面

     為了繼承而設計的類、使用者介面,應該盡少實現Serializable介面.

     java.lang.throwable實現了Serializable介面,所以RMI的異常可以從伺服器端傳到客戶端

     httpservlet實現了Serializable介面,會話狀態(session state)可以被快取。

  七十五,考慮使用自定義的序列化形式

  七十六,保護性地編寫readObject方法

  七十七,對於例項控制,列舉型別優先於readResolve

  七十八,考慮用序列化代理代替序列化例項

相關文章