一、定义
保证一个类只有一个实例,且该类能自行创建这个实例并提供一个访问该实例的全局访问点。
例如Windows 的任务管理器、回收站、操作系统中的文件系统、多线程中的线程池、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的读取配置文件的类、应用程序中的对话框、系统中的缓存等常常被设计成单例。
二、实现
- 饿汉式:线程安全,调用效率高。 但不能延时加载
public class HungarySingleton {
//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
private static HungarySingleton ins=new HungarySingleton();
private HungarySingleton() {
}
//没有同步,调用效率高
public static HungarySingleton getInstance() {
return ins;
}
}
- 懒汉式:线程安全,调用效率低。延时加载。
public class LazySingleton {
// 类初始化时,立即加载这个对象(没有延时加载的优势)
private static LazySingleton ins;
private LazySingleton() {
}
// 同步,调用效率低
public static synchronized LazySingleton getInstance() {
if (ins == null) {
ins = new LazySingleton();
}
return ins;
}
}
- 静态内部类:外部类没有static属性,则不会像饿汉式那样立即加载对象。 只有真正调用getInstance(),才会加载静态内部类。 instance是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
兼备了并发高效调用和延迟加载的优势
public class InnerClassSingleton {
private static class SingletonInstance {
private static final InnerClassSingleton ins=new InnerClassSingleton();
}
private InnerClassSingleton() {
}
//无同步
public static InnerClassSingleton getInstance() {
return SingletonInstance.ins;
}
}
- 枚举式:枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞(没有延时加载)
public enum EnumSingleton {
// 这个枚举元素,本身就是单例对象
INSTANCE;
// 添加自己需要的操作!
public void singletonOperation() {
}
}
- 防止反射和反序列化
public class FansheXuliehua implements Serializable {
private static FansheXuliehua ins = new FansheXuliehua();
private FansheXuliehua() {
//通过手动抛出异常,避免通过反射创建多个单例对象
if(ins!=null){
throw new RuntimeException();
}
}
public static FansheXuliehua getInstance() {
return ins;
}
// 反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws ObjectStreamException {
return ins;
}
}
三、应用场景
- 在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
- 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
四、如何选用
- 单例对象 占用资源少 不需要延时加载>>>枚举式 好于 饿汉式
- 单例对象 占用资源大 需要延时加载>>>>静态内部类 好于 懒汉式