Effective Java 讀書筆記

緣起

最近由於要開發中間件。可是寫了好幾個版本,都感受不怎麼好。比起一些大廠的實現,感受在代碼架構的設計,java語言的基礎知識上面仍是掌握的不夠牢靠。因而打算重讀一些比較經典的書籍。《Effective Java》就是其中一本。java

第一章

主要講解了對象的建立和銷燬。多用工廠和建造者模式,儘可能避免冗長的構造方法。其中對於單例的建立,提到了有關在單例序列化/反序列化的時候兩個個陷阱:架構

  1. 在經過私有構造器,實例化公有final靜態域的反序列化的時候若是是僅僅是繼承Serializable接口不是不夠的。
public class User implements Serializable {

    private static final long serialVersionUID = -672817526808710807L;

    private static final User user = new User();

    private String age;

    private String name;

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private User() {
        this.age = "3";
        this.name = "java";
    }

    public static User getInstance() {

        return user;
    }

    private Object readResolve() {
        return  user;
    }
}

public class SerializableDemo {


    @SuppressWarnings("resource")
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/tiny/Desktop/test.txt"));
        out.writeObject(User.getInstance());
        FileInputStream fis = new FileInputStream(new File("/Users/xujianxing/Desktop/test.txt"));  
           ObjectInputStream ois = new ObjectInputStream(fis);  
           User u = (User) ois.readObject();  

           System.out.println(u.getName());

           if(User.getInstance()==u){
               System.out.println("equals");  
           }
           else{
               System.out.println("not equals");  
           }
    }

}複製代碼

輸出not equals。
將User對象,稍稍改造:優化

package demo;

import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = -672817526808710807L;

    private static final User user = new User();

    private String age;

    private String name;

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private User() {
        this.age = "3";
        this.name = "java";
    }

    public static User getInstance() {

        return user;
    }

    private Object readResolve() {
        return  user;
    }
}
package demo;

import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = -672817526808710807L;

    private static final User user = new User();

    private String age;

    private String name;

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private User() {
        this.age = "3";
        this.name = "java";
    }

    public static User getInstance() {

        return user;
    }

    private Object readResolve() {
        return  user;
    }
}複製代碼

增長一個readResolve方法。ui

  1. 使用反射。經過AccessibleObject.setAccessible強制調用私有構造器。就像這樣:
public class SerializableDemo {
    @SuppressWarnings("resource")
    public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, IllegalArgumentException {

        Class<?> clazz = User.getInstance().getClass();

        Constructor[] constructors = clazz.getDeclaredConstructors();

        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName());
            constructor.setAccessible(true); // AccessibleObject
            System.out.println(constructor.newInstance(null));
            System.out.println(User.getInstance());
        }
    }

}複製代碼

若是要抵禦這種攻擊。能夠修改構造器,但在構造器中拋出異常不是一個好的實踐。而且會破壞final域。this

關於建造者模式

在某些狀況下,能夠利用建造者模式來大幅優化業務代碼,使得業務代碼看起來也十分優雅,而且具備很強的擴展性。好比有一個攝影師系統。攝影師的積分規則以下:spa

  1. 每一個訂單+10分;
  2. 用戶加片:每加一張+1分;
  3. 用戶評價:五星+5分,四星+1分,三星+0分,二星-5分,一星-1;
  4. 上傳獎懲積分:拍攝日當天上傳,每單+10分。
  5. 次日上傳,每單+5分。第三天上傳,每單+0分。第四天上傳,每單-5分。以此類推,每延後一天,每單-5分。第20天以後上傳,永久取消認證攝影師資格。
  6. 攝影師競標,未被用戶選中,補償2分。
  7. 攝影師做品管理上傳一套加1分。

若是直接在業務系統裏面計算代碼,一但業務改變,那麼業務中的代碼也會改變,這樣就違背了「對擴展開放,對修改關閉"的原則。使用建造者模式能夠減小對業務系統的入侵。設計

public class PointRule {

    //積分變更記錄
    private    Multimap<String, String> myMultimap =null; 

    private int changeTotal;

    public int getChangeTotal() {
        return changeTotal;
    }

    public void setChangeTotal(int changeTotal) {
        this.changeTotal = changeTotal;
    }

    private PointRule(int changeTotal,Multimap<String, String> record) {

        this.changeTotal = changeTotal;
        this.record=record;
    }


    private static final int PLUS_NEW_ORDER = 10;
    private static final int PLUS_ADD_SHEET = 1;
    private static final int PLUS_COMMENT_FIVE = 5;
    private static final int PLUS_COMMENT_FOUR = 10;
    private static final int PLUS_UPLOAD_CURRENT_DAY = 10;
    private static final int PLUS_UPLOAD_SECOND_DAY = 5;
    private static final int PLUS_BID_FAIL = 2;
    private static final int PLUS_UPLOAD_WORK = 1;
    private static final int REDUCE_UPLOAD_DELAY = -5;

    public static class Builder {
        private int total = 0;
        Multimap<String, String> myMultimap = ArrayListMultimap.create(); 
        public static Builder getInstance() {
            return new Builder();
        }

        public Builder plusNewOrder(String orderId) {
            total += PLUS_NEW_ORDER;
            this.record.put("orderId", "plusNewOrder");
            return this;
        }

        public Builder plusAddSheet(String orderId) {
            total += PLUS_ADD_SHEET;
            this.record.put("orderId", "plusAddSheet");
            return this;
        }

        public Builder plusCommentFive(String orderId) {
            total += PLUS_COMMENT_FIVE;
            this.record.put("orderId", "plusCommentFive");
            return this;
        }

        public Builder plusCommentFour() {
            total += PLUS_COMMENT_FOUR;
            this.record.put("orderId", "plusCommentFour");
            return this;
        }

        public Builder plusUploadCurrentDay() {
            total += PLUS_UPLOAD_CURRENT_DAY;
            this.record.put("orderId", "plusUploadCurrentDay");
            return this;
        }

        public Builder plusUploadSecondDay() {
            total += PLUS_UPLOAD_SECOND_DAY;
            this.record.put("orderId", "plusUploadSecondDay");
            return this;
        }

        public Builder plusBidFail(String orderId) {
            total += PLUS_BID_FAIL;
            this.record.put("orderId", "plusBidFail");
            return this;
        }

        public Builder plusUploadWork(String orderId) {
            total += PLUS_UPLOAD_WORK;
            this.record.put("orderId", "plusUploadWork");
            return this;
        }

        public Builder reduceUploadDelay(String orderId) {
            total += REDUCE_UPLOAD_DELAY;
            this.record.put("orderId", "reduceUploadDelay");
            return this;
        }

        public PointRule build() {
            PointRule pointRule = new PointRule(this.total,this.record);
            return pointRule;

        }

    }



}複製代碼

假設有一個Service:code

public class PhotographerService {


public void userConfirmSheetOrder(){


//用戶收片的邏輯。
PointRule.Builder  buidler=PointRule.Builde.getInstance();
if(當天收片){


buidler.plusCurrentDay();
}

if(修了10張片){

(for int  i=0; i<10;i++){

buidler.plusAddSheet();
}


//其餘的業務邏輯


最後獲得此次要改變的分數:
PointRule  rule=buidler.builder();


//若是要將積分變更的記錄入庫。那麼能夠循環PointRule 的map對象。
}複製代碼

對於積分變更記錄,可使用迭代器模式。當讓這個例子只是一個舉例說明。比起直接在service裏面算積分,是否是會好不少呢?中間件

相關文章
相關標籤/搜索