1. 程式人生 > 程式設計 >Effective Java理解(一)

Effective Java理解(一)

靜態工廠方法替換建構函式 - 何時替換

  1. 靜態工廠方法含有名字,它能提供更加明確的資訊
  2. 當建立物件的代價很大時,需要使用享元模式時
  3. 能返回其返回型別的任何子型別的物件(? extends T),比如java .util. collections
  4. 返回物件的類可以根據輸入引數的不同而不同。
  5. 服務提供者框架中編寫包含方法的類時,返回的物件的類不需要存在(這句話,單獨拿在這兒來說,我是懵逼的),在書中用JDBC來說明瞭一下,但解釋得十分籠統,這裡重新釋義一下服務提供者框架
    JDBC服務提供者框架關係圖.jpg
    1. Class.forName("com.mysql.jdbc.Driver");這樣一個語句會例項化一個Driver類(提供服務者實現類),並將這個類的例項註冊到DriverManager(服務提供者註冊類)。
    2. DriverManager.getConnection("jdbc:mysql://...","...","..."); 這裡通過建立連線的URL等資訊來獲取資料庫連線。DriverManager通過傳進來的url資訊判斷出你是要獲取那個服務提供者提供的服務。因為1中已經將提供服務者實現類註冊到DriverManager了,DriverManager獲取到這個服務提供者實現類物件之後,通過呼叫它的getService(mysql裡面是connect方法)方法獲取到服務具體實現類物件,返回的卻是java.sql.Connection介面物件(因為服務具體實現類實現了Connection介面),這樣把服務具體實現類物件隱藏了。提供了很好的擴充套件性。
    3. 在Java1.6的時候加入了java.util.ServiceLoader,可以通過這個類來實現服務提供者。
靜態工廠方法的常用名稱:
  1. from —— A 型別轉換方法,它接受單個引數並返回此型別的相應例項,例如:Date d = Date.from(instant);
  2. of —— 一個聚合方法,接受多個引數並返回該型別的例項,並把他們合併在一起,例如:Set faceCards = EnumSet.of(JACK,QUEEN,KING);
  3. valueOf —— from 和 to 更為詳細的替代 方式,例如:BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  4. instancegetinstance —— 返回一個由其引數 (如果有的話) 描述的例項,但不能說它具有相同的值,例如:StackWalker luke = StackWalker.getInstance(options);
  5. createnewInstance —— 與 instance 或 getInstance 類似,除了該方法保證每個呼叫返回一個新的例項,例如:Object newArray = Array.newInstance(classObject,arrayLen);
  6. getType —— 與 getInstance 類似,但是如果在工廠方法中不同的類中使用。Type 是工廠方法返回的物件型別,例如:FileStore fs = Files.getFileStore(path);
  7. newType —— 與 newInstance 類似,但是如果在工廠方法中不同的類中使用。Type 是工廠方法返回的物件型別,例如:BufferedReader br = Files.newBufferedReader(path);
  8. type —— getType 和 newType 簡潔的替代方式,例如:List litany = Collections.list(legacyLitany);
  • tips: 當建構函式的引數過多使用Builder

依賴注入替換硬連線資源的方式

如果一個類依賴於一個或多個底層資源,這些資源的行為會影響類的行為的時候,不要使用單例或靜態的實用類來實現這個類,並且不讓類直接建立這些資源。相反,將資源或工廠傳遞給構造方法(或靜態工廠或 builder 模式)。這種稱為依賴注入的實踐將極大地增強類的靈活性、可重用性和可測試性。

重寫equals方法

  1. 自反性: 對於任何非空引用 x,x.equals(x) 必須返回 true。
  2. 對稱性: 對於任何非空引用 x 和 y,如果且僅當 y.equals(x) 返回 true 時 x.equals(y) 必須返回 true。
  3. 傳遞性: 對於任何非空引用 x、y、z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,則 x.equals(z) 必須返回 true。
  4. 一致性對於任何非空引用 x 和 y,如果在 equals 比較中使用的資訊沒有修改,則 x.equals(y) 的多次呼叫必須始終返回 true 或始終返回 false。
  5. 對於任何非空引用 x,x.equals(null) 必須返回 false。
  6. 當重寫 equals 方法時,同時也要重寫 hashCode 方法。
  7. 不要讓 equals 方法試圖太聰明。如果只是簡單地測試用於相等的屬性。
  8. 在 equal 時方法宣告中,不要將引數 Object 替換成其他型別。
//正確示範
@Override 
public boolean equals(Object o) {
    if (!(o instanceof MyType))
        return false;
    MyType mt = (MyType) o;
    ...
}
複製程式碼

實現Comparable介面

  1. 使用Arrays.sort來完成排序
  2. 無論何時實現具有合理排序的值類,都應該讓該類實現 Comparable 介面。 比較 compareTo 方法的實現中的欄位值時,請避免使用<>運運算元。 相反,使用包裝類中的靜態 compare 方法或 Comparator 介面中的構建方法。
// BROKEN difference-based comparator - violates transitivity!
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1,Object o2) {
        return o1.hashCode() - o2.hashCode();
    }
};
/**
  *不要使用這種技術!它可能會導致整數最大長度溢位和 IEEE 754 浮點運算失真的危險[JLS 15.20.1,15.21.1]。 此外,由此
  *產生的方法不可能比使用上述技術編寫的方法快得多。 使用靜態 compare 方法:
*/

// Comparator based on static compare method
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1,Object o2) {
        return Integer.compare(o1.hashCode(),o2.hashCode());
    }
};
&emsp;&emsp;
//或者使用 Comparator 的構建方法:
// Comparator based on Comparator construction method
static Comparator<Object> hashCodeOrder =
        Comparator.comparingInt(o -> o.hashCode());

//java8 比較 正序
someList.sort((Message m1,Message m2) -> m1.getSendDate().compareTo(m2.getSendDate()));
複製程式碼
參考:
  1. 《effective java》3rd -- Joshua Bloch
  2. JAVA 服務提供者框架介紹