《Effective Java》第1章 建立和銷燬對象

第1條 用靜態工廠方法代替構造器

這個靜態工廠,與設計模式中的靜態工廠不一樣,這裏的靜態工廠方法,替換爲「靜態方法」比較好理解,主要就是建議編寫靜態方法來建立對象。java

使用靜態方法的好處:正則表達式

一、靜態方法有名稱,能夠確切地描述功能,好比根據哪些參數,返回哪一種類型;數據庫

二、不須要先建立對象實例,再調用方法來建立所需的對象;使用靜態方法能夠直接使用類名加靜態方法名便可調用;設計模式

三、能夠返回原類型的任何子類型對象;緩存

四、能夠經過傳入不一樣的參數,得到不一樣的返回值;框架

五、方法返回的對象所屬的類,在編寫包含該靜態方法的類時能夠不存在,好比JDBC API;工具

六、使用構造方法建立對象時的開銷,相對於使用靜態方法來講,要大得多,好比Boolean.valueOf(Stirng)與new Boolean(String);性能

缺點和不足ui

一、靜態方法中要想返回對象,那麼該對象所屬的類必須有能夠訪問的構造器,不然不能被實例化;this

二、靜態方法比較難被發現;

 

第2條 遇到多個構造器時考慮使用構建器

當類的構造器或者靜態工廠中具備較多的參數時,或者參數可能後期會增長時,那麼在設計類的時候,可使用Builder模式。

package cn.ganlixin.effective_java;

public class BuilderTest {
    public static void main(String[] args) {
        final Person person = new Person.Builder().id(1).name("ganlixin").build();
    }
}

class Person {
    private int id;
    private String name;
  
    private Person(Builder builder) {
        id = builder.id;
        name = builder.name;
    }

    public static class Builder {
      	// Builder通常來講包含有外層類相同的屬性
        private int id;
        private String name;
      
        public Builder id(int val) { this.id = val; return this; }
        public Builder name(String val)  { this.name = val;  return this; }
        public Person build() { return new Person(this); }
    }
}

  

  上面的代碼,使用Lombok的話,只須要使用一個@Builder註解便可:

package cn.ganlixin.effective_java;

import lombok.Builder;

public class LombokBuilder {

    public static void main(String[] args) {
        final Person person = new Person.PersonBuilder().id(1).name("ganlixin").build();
    }
}

@Builder
class Person {
    private int id;
    private String name;
}

  

第3條:用私有構造器或者枚舉類型強化Singleton屬性

  單例模式:

  一、經過私有化構造器,能夠防止客戶端使用new關鍵字建立對象;

  二、提供靜態方法獲取單例對象;

  以上兩點存在的問題,可使用AccessibleObject.setAccessible()方法經過反射機制調用構造器,能夠在構造其中判斷是否已經建立實例,若是已建立,則拋出異常;

  三、單例對象的序列化問題;

  使用枚舉類型建立單例屬性,例子以下:

public class SingletonEnum {
    public static void main(String[] args) {
        final OneDemo instance = OneDemo.INSTANCE;
        instance.test();
    }
}

enum OneDemo {
    INSTANCE;
    
    // 定義其餘function
    public void test() {
        //  .........
    }
}

  

第4條:經過私有構造器強化不可實例化的能力

當咱們建立工具類的時候,通常來講工具類不該該被實例化,雖然能夠將工具類聲明爲抽象類來避免被實例化,可是這樣並很差,由於抽象類的子類能夠實例化!

解決方案:顯示聲明工具類的無參構造方法,在其中拋出異常便可。

class MyUtil {

    private MyUtil(){ throw new RuntimeException("工具類不該該被實例化"); }

    // 聲明工具類中的方法
    public static void myMethod(){}
}

  

 第5條:優先考慮依賴注入來引用資源

  主要就是講依賴注入。

  問題是這樣的:有一個郵件白名單,對於接收到的郵件,須要先進行過濾一遍(匹配白名單),那麼可能會寫出下面的代碼:

// 郵件過濾器
class MailChecker{
    
    // 白名單列表,在代碼中寫死
    private List<String> whiteList = new ArrayList<String>(){{
        add("123"); add("456"); add("789");
    }};
    
    // 進行檢驗操做
    public boolean isValid(String email) {
        return whiteList.contains(email);
    }
}

  上面的代碼其實並很差,緣由:

  一、過濾名單寫死了;

  二、只能進行郵件過濾;這一點可能會有疑惑,功能專注很差嗎?這沒問題,可是若是有一個「電話過濾器」是否是又要有一個PhoneChecker呢?

  推薦作法:在過濾器中不寫死whiteList,白名單由外部傳入,過濾器的功能就是進行過濾而已,改成以下:

public class UseInject {
    public static void main(String[] args) {
        // 假設phoneList是外界傳入的,或者是文件中讀入的
        List<String> phoneList = null;
        Checker phoneChecker = new Checker(phoneList);
        phoneChecker.isValid("123");
    }
}

class Checker{
    private List<String> whiteList;
    
    // 接收外界傳入的白名單(能夠是電話、郵箱、地址....)
    public Checker(List<String> whiteList) {
        this.whiteList = whiteList;
    }
    
    // 檢驗操做
    public boolean isValid(String val) {
        return whiteList.contains(val);
    }
}

  若是是使用Spring這些框架,能夠直接利用依賴注入,好比:

public class UseInject {
    
    @Value("${phoneList}")
    private static List<String> phoneList;

    public static void main(String[] args) {
        Checker phoneChecker = new Checker(phoneList);
        phoneChecker.isValid("123");
    }
}

  

第6條:避免建立沒必要要的對象

  一、雖然Java有自動裝箱和自動裝箱,可是請儘可能使用基本數據類型;

  二、對於提供了靜態工廠方法和構造器的類來講,應該優先使用靜態方法而非構造器建立對象;

  三、有一些對象不該該被重複建立,好比正則表達式的Pattern實例;

  四、應該避免維護本身的對象池,數據庫鏈接池除外,由於創建數據庫鏈接的代價比較大,建立普通對象的代價則相對來講很小;

  五、不要太死板,有些時候,爲了不建立沒必要要的對象,會付出更多的代價;

 

第7條:消除過時的對象引用

  一、若是類是本身管理內存,那麼就須要須要警戒內存泄漏問題,一旦元素被釋放掉,則該元素包含的任何對象應用都應該被清空;

  二、緩存致使內存泄漏,可使用WeakHashMap;

  三、若是提供服務給客戶端調用,要警戒客戶端的反覆調用是否會反覆產生對象,可是卻沒有清理垃圾;

 

第8條:避免使用終結方法和清除方法

  一、終結方法(finalizer)和清除方法(cleaner)不能保證會被及時的執行,從一個對象變爲不可達開始,到執行這兩個方法的事件是任意長的,因此,注重時間的任務不該該由finalizer和cleaner來完成;

  二、永遠不該該依賴finalizer和cleaner方法來更新重要的持久狀態,由於這兩個方法可能沒有機會被執行;

  三、System.gc和System.runFuinalization只能增長finalizer和cleaner的執行機會,注意,增長可能性,但不是保證,100%;

  四、finalizer中出現異常,終結過程會終止,可怕吧,想死還死不掉;

  五、下降程序性能、finalizer attack;

 

第9條:try-with-resource優先於try-finally

  要使用try-with-resource,須要類實現AutoCloseable接口(大多數類都實現了)。

  下面以書中的例子舉例:

public static void notSuggest() throws IOException {
    BufferedReader bufferedReader = null;
    try {
        bufferedReader = new BufferedReader(new FileReader("demo.txt"));
        bufferedReader.readLine();
    } finally {
        bufferedReader.close();
    }
    
    // 注意,上面的demo.txt不存在時,報異常FileNotFoundException,而後finally中的close也會報錯,可是close的異常會覆蓋readLine的異常
    // 因此調用notSuggest()方法,只會看到close的異常Exception in thread "main" java.lang.NullPointerException
}

  改爲下面這樣就能夠捕獲全部異常了,可是看起來,代碼好臃腫:

public static void notSuggest2() {
    BufferedReader bufferedReader = null;
    try {
        bufferedReader = new BufferedReader(new FileReader("demo.txt"));
        bufferedReader.readLine();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 雖然能夠打印出全部的異常信息(FileNotFoundException和close的NullPointerException異常)
    // 按照常識,若是前面一步已經錯了,就不該該再走下去,可是FileNotFoundException以後,仍是會執行finally中的close,結果又出現異常
}

  推薦使用try-with-resource

public static void suggest() {
    try (BufferedReader bufferedReader = new BufferedReader(new FileReader("demo.txt"))) {
        bufferedReader.readLine();
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 不須要catch每個子異常,好比FileNotFoundException,直接寫一個IOException或者Exception便可
    // 上面demo.txt不存在,因此會出現java.io.FileNotFoundException,可是沒有報第二個異常哦
}
相關文章
相關標籤/搜索