设计模式之单例模式

单例模式(Singleton Pattern)

意图:

  Ensure a class has only one instance,and provide a global point of access to it.

  保证一个类仅有一个实例,并提供一个可访问它的全局访问点。

动因:

  有时候,对于一个类来说只有一个实例是很重要的,如页面的访问量计数器。一个全局变量可以被访问,但如何才能保证全局只有一个实例?

Singleton模式是设计模式中最简单的模式之一,它只需考虑一个类并保证它只有一个实例,基本类图如下:

 要素:1、私有的构造方法。2、私有的静态引用(instance)。3、返回自身实例的公开静态方法(getInstance)

一个简单的单例类如下:

public class Singleton {
    private static Singleton instance = null;

    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(instance==null){
            instance = new Singleton();
        }
        return instance;
    }

    private int count = 0;
    public void doSomething(){
        System.out.println(count++);
    }
}

 

其中synchronized关键字保证线程安全,但是以牺牲性能为代价。在java中,这叫懒汉模式单例,其还可以改为如下形式:

/**
 * 使用volatile关键字
 */
public class Singleton{
    private volatile static Singleton instance = null;
    private Singleton(){}

    public static Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                if(instance==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

 

voaltile关键字在jdk1.5之后出现,配合synchronized保证线程安全

此外,还有一种饿汉式的单例模式,在声明instance时直接实例化:

/**
 * 饿汉式的单例模式
 */
public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
    
    private int count = 0;
    public void doSomething(){
        System.out.println(count++);
    }
}

 

饿汉式的单例天生线程安全,但由于在类加载时便进行实例化,会带来类加载时的额外消耗 。这里推荐一种静态内部类延迟加载的方法:(内部静态在有引用之后才被加载到内存中,即在调用getInstance后)

public class Singleton{
    private Singleton(){}
    private static class LazyClassHolder{
        private static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return LazyClassHolder.INSTANCE;
    }
}

 

 此外还有在effective java中推荐的使用枚举单例:

/**
 * 枚举的方式,effective java中推荐的
 */
public enum Singleton{
    INSTANCE;

    private int count = 0;
    public void doSomething(){
        System.out.println(count++);
    }
}

 

枚举的方式还能防止通过反序列化重新创作对象。但是在Android中不推荐使用枚举单例(似乎会占更多内存)

在java中,一般认为饿汉模式优于懒汉模式,C++中则懒汉模式更好。

至于单例模式长久不用会不会被JVM回收,这个倒没遇到过,以后遇到再说吧- - 

另外,通过反射,可以在程序中获得新的实例:

Class c = Class.forName(Singleton.class.getName());
Constructor constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance = (Singleton)constructor.newInstance();

 

 测试可以得到新的实例,因此在程序中,尽量避免使用反射来获得实例。

 

posted on 2016-05-04 11:16  d_pay  阅读(156)  评论(0)    收藏  举报

导航