美文网首页
Effective Java读书笔记--第2章 创建和摧毁对象

Effective Java读书笔记--第2章 创建和摧毁对象

作者: Huigesi | 来源:发表于2018-10-08 16:24 被阅读39次

个人读书笔记,部分没读懂的知识点可能会简单概括或缺失,以后反复阅读后再完善。

第二章 创建和摧毁对象

第1条:使用静态工厂方法替代构造器

静态工厂方法的好处。

1、有命名,更方便阅读。
2、不用每次调用都创建新对象(构造器需要)。
3、可以返回原返回类型的任何子类型对象(更灵活)。
4、创建参数化类型实例使代码更简洁。
例子:

public static <K, V> HashMap<K, V> newInstance() {
        return new HashMap<>();
}
    
Map<String, List<String>> m =Singleton.newInstance();

静态工厂方法的坏处。

1、类如果不含有公有或者受保护的构造器,就不能被子类化。
2、与静态方法没有区别(不方便阅读?)

第2条:遇到多个构造器时要考虑用构建器

类中有多个参数,一般使用的重叠构造器(telescoping constructor)模式有不好的地方,比如有些不想设置的参数也不得不传值。

解决办法提到了javaBeans模式与Builder模式。

javaBeans模式:

public class NutritionFacts {
    private int servingSize = -1;
    private int servings = -1;
    private int calories = 0;
    private int fat = 0;
    private int sodium = 0;
    private int carbohydrate = 0;

    public NutritionFacts() {
    }

    public void setServingSize(int servingSize) {
        this.servingSize = servingSize;
    }

    public void setServings(int servings) {
        this.servings = servings;
    }

    public void setCalories(int calories) {
        this.calories = calories;
    }

    public void setFat(int fat) {
        this.fat = fat;
    }

    public void setSodium(int sodium) {
        this.sodium = sodium;
    }

    public void setCarbohydrate(int carbohydrate) {
        this.carbohydrate = carbohydrate;
    }
}

Builder模式:

public class NutritionFacts {
   
    private int servingSize;
    private int servings;
    private int calories;
    private int fat;
    private int sodium;
    private int carbohydrate;

    public static class Builder {
        private final int servingSize;
        private final int servings;

        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder setCalories(int calories) {
            this.calories = calories;
            return this;
        }

        public Builder setFat(int fat) {
            this.fat = fat;
            return this;
        }

        public Builder setSodium(int sodium) {
            this.sodium = sodium;
            return this;
        }

        public Builder setCarbohydrate(int carbohydrate) {
            this.carbohydrate = carbohydrate;
            return this;
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize=builder.servingSize;
        servings=builder.servings;
        calories=builder.calories;
        fat=builder.fat;
        sodium=builder.sodium;
        carbohydrate=builder.carbohydrate;
    }
}

Builder模式调用代码:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                    .setCalories(100)
                    .setSodium(35)
                    .setCarbohydrate(27)
                    .build();

如果类的构造器或者静态工厂中具有多个参数,推荐Builder模式。

第3条:用私有构造器或者枚举类型强化Singleton属性

Singleton指仅仅被实例化一次的类。

java 1.5以前,实现Singleton的两种方法:

public class Elvis {
    private static final Elvis INSTANCE = new Elvis();

    private Elvis() {}
    
    private static Elvis getInstance(){
        return INSTANCE;
    }

    public void leaveTheBuilding() {}
}

或:

public class Elvis2 {
    private static Elvis2 ourInstance = new Elvis2();

    public static Elvis2 getInstance() {
        return ourInstance;
    }

    private Elvis2() {
    }
    public void leaveTheBuilding() {}
}

第二个方法(工厂方法)更灵活。使用泛型更优(第27条)
java 1.5之后实现Singleton的第三种方法:

public enum Elvis3 {
    INSTANCE;
    public void leaveTheBuilding() {}
}

第4条:通过私有构造器强化不可实例化的能力

有些工具类不希望被实例化,企图通过将类做成抽象类来强制该类不可被实例化是行不通的。

可以让这个类包含有私有构造器:

public class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}

该做法的副作用:使得一个类不能被子类化。

第5条:避免创建不必要的对象

最好能重用对象,而不是每次需要的时候创建一个相同功能的新对象。
极端例子:

String s = new String("stringette");

如果上面这种写法用在循环中,会创建成千上万不必要的String实例。
正常写法:

String s = "stringette";

有些已知不会被修改的可变对象,也可以重用。
重用前例子:

class Person{
    private final Date birthDate;

    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }

    /*
     * 效率低下的方法
     * */
    public boolean isBabyBoomer() {
        Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart=gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd=gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0 &&
                birthDate.compareTo(boomEnd) < 0;
    }
}

重用后例子(调用速度明显增快):

class Person{
    private final Date birthDate;

    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
    /*
     *改进后的做法(快了250倍)
     * */
    private static final Date BOOM_START;
    private static final Date BOOM_END;
    static {
        Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START=gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END=gmtCal.getTime();
    }

    public boolean isBabyBoomer2() {
        return birthDate.compareTo(BOOM_START) >= 0 &&
                birthDate.compareTo(BOOM_END) < 0;
    }
}    

要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。

例子:

public static void main(String[] args) {
        Long sum=0L;
        for (long i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }
        System.out.println(sum);
    }

sum的声明是Long而不是long,意味着程序构造了许多的多余的Long实例。运行时间大大降低。

第6条:消除过期对象的应用

一个存在内存泄露的程序:

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        //这样写会有内存泄漏
        return elements[--size];
        /*
        * 清空过期的引用
        * */
        //Object result = elements[--size];
        //elements[size]=null;
        //return result;
    }

    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

代码中通过清空过期应用解决内存泄漏:

Object result = elements[--size];
    elements[size]=null;
    return result;

第7条:避免使用过期方法

讲终结方法(finalizer)通常不可预测,一般情况下是不必要的。
使用终结方法还会有性能损失。

相关文章

网友评论

      本文标题:Effective Java读书笔记--第2章 创建和摧毁对象

      本文链接:https://www.haomeiwen.com/subject/cpdjaftx.html