单例模式的目的
创建对象,确保对象的唯一性,在同一个系统中,对象只有一个实例.让类自己负责保存它的唯一实例,提供一个访问实例的方法.
单例模式概述
单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
要点
- 类只能有一个实例
- 类自己负责创建实例
- 提供访问实例的方法
实现分类
- 饿汉式
- 懒汉式
饿汉式
pulic class Singleton{
//唯一实例
private static Singleton instance = new Singleton();
//只能由类自己负责创建实例
private Singleton(){
}
}
//外部访问实例的方法
public static getInstance(){
return instance;
}
从代码中可以看出,在定义静态变量的时候就实例化了单例类.在类加载的时候,静态方法会先加载,所以单例对象在类加载的时候九被创建了,静态变量只加载一次,可以保证单例对象的唯一性,并且是线程安全的.但是在类加载的时候就创建,会影响程序的效率.
懒汉式
public class Singleton{
//唯一实例
private static Singleton instance = null;
//只能由类自己负责创建实例
private Singleton(){
}
//外部访问实例的方法
public static getInstance(){
if (interface == null)
instance = new Singleton();
}
return instance;
}
}
懒汉式在需要的时候才实例化,在类加载的时候不进行实例化,能起到懒加载的作用,但是如果在多线程的情况下,比如线程A和线程B同时调用getInstance(),可能会导致并发问题,所以要在getInstance()前加上synchronized进行同步,但是这样每次都要判断,会降低访问速度.可以用双重检查锁.
双重检查锁的第一次检查是检查实例是否存在,如果不存在再进入下面的同步块,而不是直接进入同步块.第二重检查是进入同步块后,再检查实例是否存在,如果不存在,就在同步的情况下创景实例.
/**
*双重检查锁的单例模式
*/
public class Singleton{
/**
* 对保存实例的变量添加volitile的修饰
* 唯一实例
*/
private volatile static Singleton instance = null;
//只能由类自己负责创建实例
private Singleton(){
}
//外部访问实例的方法
public static getInstance(){
//第一重检查,检查实例是否存在,如果不存在,再进入同步代码块
if (interface == null)
//锁定代码块
synchronized (Singleton.class){
//第二重检查
if (instance == null) {
instance = new Singleton();//创建单例实例
}
}
}
return instance;
}
}
更好的实现方式
饿汉单例模式在类加载的时候就会创建,不能实现延时加载,如果以后不用,还会占据内存,还会影响程序的加载效率.懒汉单例模式需确保线程安全,性能会受到影响.比较好的实现方式是结合二者的优点,使用静态内部类来实现单例模式.
public class Singleton {
/**
* 类级的内部类,也就是静态类的成员式内部类,
*该内部类的实例与外部类的实例没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
/**
* 私有化构造方法
* 只能由类自己负责创建实例
*/
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
使用静态内部类,能实现延时加载,保证线程安全,又不影响系统性能.
枚举类型实现
根据<Effective Java>第二版,单元素的枚举类型已经成为实现Singleton的最佳方法。
/**
* 使用枚举来实现单例模式
*
*/
public class Singleton {
/**
* 定义一个枚举的元素,它就代表了Singleton的一个实例
*/
uniqueInstance;
/**
* 单例的方法
*/
public void singletonOperation(){
//功能树立
}
}
单例的使用场景
系统只需要要一个实例对象,而且客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。