Java性能优化(2):使用私有构造函数强化singleton属性

singleton是指这样的类,它只能实例化一次。singleton通常被用来代表那些本质上具有唯一性的系统组件,比如视频显示或者文件系统。
实现singleton有两种方法。这两种方法都要把构造函数保持为私有的,并且提供一个静态成员,以便允许客户能够访问该类唯一的实例:在第一种方法中,公有静态成员是一个final域:

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

    }
}

私有构造函数仅被调用一次,用来实例化公有的静态final域Elvis.INSTANCE,由于缺少公有的或者受保护的构造函数,所以保证了Elvis的全局唯一性,一旦Elvis类被实例化之后,只有一个Elvis实例存在——不多也不少,客户的任何行为都不会改变这一点。

第二种方法提供了一个公有的静态工厂方法,而不是公有的静态final域:

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

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

所有对于静态方法Elivis.getInstance的调用,都会返回同一个对象引用,所以,不会有别的Elvis实例被创建。
第一种方法的主要好处在于,组成类的成员声明很清楚的表明了这个类是一个singleton:公有的静态域是final的,所以该域将总是包含相同的对象引用。第一种方法可能在性能上稍微领先,但是在第二种方法中,一个优秀的JVM实现应该能够通过静态工厂方法的调用内联化,来消除这种差别。
第二种方法的主要好处在于,它提供了灵活性:在不改变API的前提下,允许我们改变想法,把该类做成singleton,或者不做成singleton。singleton的静态工厂方法返回该类的唯一实例,但是,它也很容易被修改。比如说,为每个调用该方法的线程返回一个唯一的实例。
总而言之,如果你确信该类将永远是一个singleton,那么使用第一种方法是有意义的。如果你希望保留一点余地,那么还有一招。
为了使一个singleton类变成可序列化的(serializable),仅仅在声明中加上“implements serializable”是不够的,为了维护singleton性,你必须也要提供一个readResolve方法。否则的话,一个序列化的实例在每次反序列化的时候,都会导致创建一个新的实例,比如说,在我们的例子中,会导致“假冒的Elvis“。为了防止这种情况,在Elvis类中加入下面的readResolve方法:

“`
private Object readResolve() throws ObjectStreamException
{
return INSTANCE;
}

在两种情况下,私有的构造函数与公有的静态成员联合起来,以确保当该类被初始化之后,不会再有新的实例被创建。在本条目的情形下,该类只有一个实例被创建;枚举类型的每一个成员都有一个实例被创建。这种方法会被进一步发挥:不存在公有的构造函数,以确保一个类永远也不会有实例被创建。

posted on 2015-08-20 09:13  爱你一万年123  阅读(213)  评论(0编辑  收藏  举报

导航