01 Effective Java 系列-创建和销毁对象

来源:互联网 发布:淘宝能直接微信支付吗 编辑:程序博客网 时间:2024/06/05 06:14

01 Effective Java 系列-创建和销毁对象

一、考虑静态工厂代替构造器

public class NewType {    public static NewType valueOf(){        return new NewType();    }}

上面是一个简单的静态工厂方法,作为生成类的实例方法,静态工厂模式应该作为程序员常用的方法。相比于构造函数模式,静态工厂方法有以下优点:

1.1 静态工厂方法有名称

静态工厂方法有名字,可以见文知意,而构造函数名称都一致,只是参数不同,在调用时容易出错,而静态工厂方法可以根据需求设置不同的名称。

1.2 静态工厂方法不必在每次调用的时候都创建一个新对象

可以使得不可变类可以预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象,比较适合单例类。

1.3 静态工厂方法可以灵活的法案会类型的任何子类型的对象。

可以根据传入进来的不同参数生成不同的子类对象,使实例的使用更加灵活。

二、构造函数多个参数时,考虑使用构造器模式

静态工厂方法和构造函数都有个共同的局限性,就是对于大量的可选参数时,代码编写起来较为复杂,一般采用重叠构造器模式,也即一个参数一个构造函数,两个参数再一个构造函数,三个参数再一个构造函数,以此类推,参数较多时,整个构造函数较为复杂,容易出错。大量的可选参数实例化对象除了静态工厂方法和构造函数,比较常见的有JavaBeans模式,相信很多程序员在每天的工作中也会编写不少的JavaBean类。
public class Student {    private String name;    private int age;    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}
JavaBeans模式有个问题就是将实例的构造过程分布到几个过程中,容易造成参数的不一致,对于参数的有效性校验不是很方便,另外JavaBeans模式不能用于不可变类,需要程序要额外维护线程安全。还有一种较好的方式用于创建对象就是构建器Builder模式,既能保证参数的有效性,同时代码也具有良好的可读性。
public class Student {    private String name;    private int age;    private String sex;    public static class Builder{        String name;        int age;        String sex;        public Builder name(String name){            this.name = name;            return this;        }        public Student build(){            return new Student(this);        }    }    private  Student(Builder builder){        name = builder.name;        age = builder.age;        sex = builder.sex;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }}
构建器模式可以使用一个builder构建多个对象,且每次创建的对象都是不同的对象。

三、用私有构造函数和枚举类型强化Singleton属性

Singleton是指仅仅被实例化一次的类,主要用于单例模式。创建单例模式要注意的是如果单例类要实现可序列化,必须所有的参数都是瞬时(transient)的,并且提供readResoulve方法,否则每次反序列化一个序列化的实例,都会创建一个新的实例。
public class Elvis {    //公有静态对象    public static final Elvis INSTANCE = new Elvis();    private Elvis(){};    //静态工厂方法    public static Elvis getInstance(){        return INSTANCE;    }    private Object readResolve(){        return INSTANCE;    }}
上述方式创建的单例对象可以使用发射暴力破解,获取构造函数创建对象,所以比较好的生成单例对象的方法是采用枚举类型。
public enum Elivi{        INSTANCE;}

四、消除过期的对象引用

考虑下面简单的栈的实现。

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;    }    private void ensureCapacity() {        if(elements.length == size){            elements = Arrays.copyOf(elements,2*size+1);        }    }    public Object pop(){        if(size == 0){            throw new EmptyStackException();        }        return elements[--size];    }}

上述程序运行起啦没有问题,但是存在一个隐藏问题就是内存泄漏的问题,因为栈先是增长,后是收缩,从栈中弹出的对象不会被当做垃圾回收,因为栈的内部维护这这些对象的过期引用。过期引用就是指永远不会被解除的引用。
解决上述问题的方法就是当一个对象从栈中弹出之后,指向它的引用就过期即可。代码如下

public Object pop(){    if(size == 0){        throw new EmptyStackException();    }    Object result = elements[--size];    elements[size] = null;    return result;}
并不是所有的类都需要清除对象过期引用,一般来说只有类自己管理内存的时候后,才需要清除过期引用,这时程序员需要警惕内存泄漏问题。
0 0
原创粉丝点击