Effective Java阅读笔记(一)——第二章 创建和销毁对象

第1条 考虑用静态工厂方法代替构造器

使用静态工厂方法有以下优点:

1.当一个类需要多个带有相同签名的构造器时,静态工厂方法的名称能够更好的突出它们之间的区别;

2.不必在每次调用时都创建一个新的对象;

3.在创建参数化类型的实例时,使代码更加简洁:

假设HashMap提供了静态工厂方法:

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

代替Map<String ,List<String>> m = new HashMap<String ,List<String>>();


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

当构造参数过多时,有以下几种创建方法:
1.采用重叠构造:写多个构造方法,每个方法的参数数量依次增加,在参数少的构造器中调用参数多的构造器,并用默认值传递多出来的参数。这种方法在参数太多时代码量太大;
2.采用JavaBeans模式,即调用一个无参构造器来创建对象,并用setter方法来设置参数。这种方法的构造过程被分为多次调用,可能使得javaBean处于不一致的状态。
3.Builder模式
示例:

public class NutritionFacts {
    private final int servingSize;
    private final int serving;
    private final int calories;
    private final int fat;
    private final int sodium;

    public static class Builder{
        private int servingSize;
        private int serving;
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;

        public Builder(int servingSize,int serving){
            this.servingSize = servingSize;
            this.serving = serving;
        }
        public Builder calories(int val){
            calories = val;
            return this;
        }

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

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

        public NutritionFacts build(){
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder){
        servingSize = builder.servingSize;
        serving = builder.serving;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
    }
}
创建方法:
NutritionFacts cocaCola = new Builder(240,8).calories(100).sodium(27).build();


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

Singleton的实现方式主要有三种:
1.公有静态成员是个final域

Public class Elvis{
    public static final Elvis INSTANCE = new Elvis();
    private Elvis(){ …… }
    public void leaveTheBuilding(){ …… }
}

2.公有的成员是个静态工厂方法
Public class Elvis{
    private static final Elvis INSTANCE = new Elvis();
    private Elvis(){ …… }
    public static Elvis getInstance(){return INSTANCE;}
    public void leaveTheBuilding(){ …… }
}

以上两种方法中,享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器,如果要抵御这种攻击,可以在被要求创建第二个实例的时候抛出异常。

3.包含单个元素的枚举类型,这种方法提供了序列化机制,绝对防止多次实例化,是实现Singleton的最佳方法。
Public enum Elvis{
    INSTANCE;
    public void leaveTheBuilding(){ …… }
}


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

有些类不希望被实例化,如工具类等,实例化对它没有任何意义,若不加以限制,可能会被无意识的实例化,因为在缺少构造器的情况下,编译器会自动提供一个公有的、无参的缺省构造器。
因此我们只要让这个类包含私有构造器,它就不能被实例化了。


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

1.对于String:
String s = new String(“stringette”); //DO NOT DO THIS!
“stringette”本身就是一个String实例,这样每次执行这条语句就会创建一个新的对象
应该用String s = “stringette”;来代替,这样只要他们包含相同的字符串字面常量,该对象就会被重用。

2.重用那些已知不会被修改的可变对象
示例:

public class  Person{
        private final Date birthday;
        public boolean isBabyBoomer(){
            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            gmtCal.set(1946, Calendar.JANUARY,1,0,0,0);
            Data boomStart = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY,1,0,0,0);
            Data boomEnd = gmtCal.getTime();
            return  birthday.compareTo(boomStart)>=0 && birthday.compareTo(boomEnd)<0;
        }
    }
上述代码中,每次调用isBabyboomer时,都会新建一个Calendar、一个TimeZone和两个Date实例,这是不必要的。优化如下:
public class  Person{
        private final Date birthday;
		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);
            Data boomStart = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY,1,0,0,0);
            Data boomEnd = gmtCal.getTime();
}
        public boolean isBabyBoomer(){
            return  birthday.compareTo(boomStart)>=0 && birthday.compareTo(boomEnd)<0;
        }
    }
将这些不变的可变量放到static中,这样只会在类初始化的时候才执行一次。若isBabyBoomer被频繁调用,这种方法性能会显著提高。


3.优先使用基本类型而不是装箱基本类型

public static void main(String[] args){
    Long sum = 0L;
    for(int i=0;i<Integer.MAX_VALUE;i++){
        Sum += i;
    }
}
这段计算所有int正值总和的程序,若用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];
    }
    
    private void ensureCapacity(){
        if(elements.length == size){
            elements = Arrays.copyOf(elements,2*size+1);
        }
    }
}
上面的代码是一个简单的栈实现,其存在内存泄露!当调用pop方法后,栈中存在过期的引用。
如果一个栈先增长,然后收缩,那么从栈中弹出的对象将不会被当做垃圾回收,需要手动回收:
public Object pop(){
        if(size == 0){
            throw new EmptyStackException();
        }
		Object result = elements[--size];
		elements[size] = null;
        return result;
}

第7条 避免使用终结方法

finallizer方法是不能保证会被及时地执行的,注重时间的任务不应该由finallizer方法来完成,一般情况也是不必要使用的,应该尽量避免使用它。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值