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里面算积分,是否是会好不少呢?中间件

相关文章
相关标签/搜索