设计模式之单例模式
单例模式(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();
测试可以得到新的实例,因此在程序中,尽量避免使用反射来获得实例。
浙公网安备 33010602011771号