我们也经常遇到类似的情况,为了节约系统资源,有时需要确保系统中某个类只有唯一一个实例,当这个唯一实例创建成功之后,我们无法再创建一个同类型的其他对象,所有的操作都只能基于这个唯一实例。为了确保对象的唯一性,我们可以通过单例模式来实现,这就是单例模式的动机所在。
定义:
单例模式(Singleton
Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
那么,怎么创建一个单例对象呢?
首先我们可以通过三个步骤来创建:
1、由于每次使用new关键字来实例化一个类时都将产生一个新对象,为了确保实例的唯一性,我们需要禁止类的外部直接使用new来创建对象,因此需要将该类的构造函数的可见性改为private,如下代码所示:
/* 私有构造方法,防止被实例化 */ private Singleton() { }
2、将构造函数改为private修饰后该如何创建对象呢?不要着急,虽然类的外部无法再使用new来创建对象,但是在该类的内部还是可以创建的,因此,我们可以在类中创建并保存这个唯一实例。为了让外界可以访问这个唯一实例,需要在TaskManager中定义一个静态的TaskManager类型的私有成员变量,为了保证成员变量的封装性,我们将Singleton 类型的singleton对象的可见性设置为private,如下代码所示:
private static Singleton singleton = null;
3、但外界该如何使用该成员变量并何时实例化该成员变量呢?答案是增加一个公有的静态方法,如下代码所示:
/* 静态工程方法,创建实例 */ public static Singleton getInstance() { if (singleton == null) { // 必需加这个判断,不然每次调用getInstance()都会new出来一个新对象,使其指向新对象,原来的数据会没了被重新初始化了。或者private static Singleton singleton =new Singleton(),就不用判断了,直接返回singleton synchronized (singleton) {//同步代码块,在多线程环境中保证线程安全 if (singleton == null) {//要加if判断语句,因为当有两个线程A、B同时进入第一个if判断语句中时,如果同步代码块里面没有if判断语句,则A线程进入synchronized加锁new出个对象后解锁,B线程进入synchronized也会new出个对象,这样就有线程安全问题 singleton = new Singleton(); } } } return singleton; }
在getInstance()方法中首先判断singleton对象是否存在,如果不存在(即singleton ==
null),则使用new关键字创建一个新的Singleton类型的singleton对象,再返回新创建的singleton对象;否则直接返回已有的singleton对象。
在类外我们无法直接创建新的Singleton对象,但可以通过代码Singleton.getInstance()来访问实例对象,第一次调用getInstance()方法时将创建唯一实例,再次调用时将返回第一次创建的实例,从而确保实例对象的唯一性。
单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
那么来看一个完整的单例模式代码:
public class Singleton { private static Singleton singleton = null;//static方法中不能用非static全局变量,赋值为null作用是延迟创建对象 /* 私有构造方法,防止被实例化 */ private Singleton() { } /* 静态工程方法,创建实例 */ public static Singleton getInstance() { if (singleton == null) { // 必需加这个判断,不然每次调用getInstance()都会new出来一个新对象,使其指向新对象,原来的数据会没了被重新初始化了。或者private static Singleton singleton =new Singleton(),就不用判断了,直接返回singleton synchronized (singleton) {//同步代码块,在多线程环境中保证线程安全 if (singleton == null) {//要加if判断语句,因为当有两个线程A、B同时进入第一个if判断语句中时,如果同步代码块里面没有if判断语句,则A线程进入synchronized加锁new出个对象后解锁,B线程进入synchronized也会new出个对象,这样就有线程安全问题 singleton = new Singleton(); } } } return singleton; } }