什么是单例模式
单例模式,顾名思义,就是整个系统就只有一个实例存在。
特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
讲的通俗一点,我们拿女娲造人来举例:
我们先写一个女娲类:
public class N_Wa { }
很明显,任何人都不能创造女娲,所以女娲的构造应该是私有的:
public class N_Wa { private N_Wa() {} // 构造方法私有化 }
有人可能要质疑:那女娲应该是谁创造的?
这个问题千百万年来都没有人能解开过,所以在我们的潜意识中认为:神是自己创造自己的!
public class N_Wa { private static final N_Wa n_wa = new N_Wa(); // 神自己创造自己 private N_Wa() {} // 构造方法私有化 }
private 保证了女娲的私有性,毕竟这个世界上没有人见过女娲。
static 保证了女娲的静态性,她与类共存亡。
final 保证了女娲是“终极”常量,不可能再被修改。
女娲已经出来了,接下来干什么呢?
当然就是请她开始造人啦:
public class N_Wa { private static final N_Wa n_wa = new N_Wa(); // 神自己创造自己 private N_Wa() {} // 构造方法私有化 public static N_Wa getInstance() { // 请求女娲 return n_wa; } }
getInstance 和 new?
new 是重新创建一个对象,且只能单次使用。
getInstance 是没有对象的时候创建对象,有了之后就保留在内存中,下次就不用再重新创建了,因此它的对象一定是static的。
这样一来,只需要调用 N_Wa.getInstance(); 女娲就请过来了,而且不论是谁,请来的都是同一个女娲,也就是我们构造的“终极”常量:n_wa。当然你还可以往里面加入其它的功能(造人功能未写出)。
到此为止,我们就已经学会了单例模式——“饿汉模式”,即我们先把女娲给造好,需要的时候直接用就好了。
随之而来的是另一个问题:要是我们根本没有用到女娲呢?那不是白造了?于是又出现了一种叫“懒汉模式”:
public class N_Wa { private static N_Wa n_wa; // 这里并没有new一个女娲 private N_Wa() {} // 构造方法私有化 public static N_Wa getInstance() { // 请求女娲 if(n_wa == null) { n_wa = new N_Wa(); } return n_wa; } }
可以发现,我们不再是提前造女娲了,而是需要的时候再去造她(实例化),但这样子的坏处就是每次都要重新造一次女娲,所以速度肯定不如之前的“饿汉模式”。而且有个大问题,就是一旦有很多人同时请女娲的话,依然可能造成多个神的情况。
所以我们让这些人排队?看起来挺有道理,但仔细想想,有些人做事总是拖拖拉拉,毛手毛脚的,万一给让它排到前面去了,那后面的人不得等半天吗?等下队伍直接排出地球外了,所以我们干脆让他们直接抢,谁抢到就是谁的,这样一直抢下去:
public class N_Wa { private static N_Wa n_wa; // 这里并没有new一个女娲 private N_Wa() {} // 构造方法私有化 public static N_Wa getInstance() { // 请求女娲 if(n_wa == null){ // 代表还没有人抢到 synchronized (N_Wa.class) { // 放他们进去抢 if(n_wa == null) { // 第一个抢到的给他 new一个然后返回 n_wa = new N_Wa(); } } } return n_wa; } }
synchronized 是一种同步锁,可以防止同步发生,通俗来讲就是第一个人先用,这时锁被锁上,等他用完,锁再打开,以此类推。只有当锁是开着的,才能轮到下一个人。
到此为止,最基础的两种“恶汉模式”和“懒汉模式”就完成了,单例模式还有其它的一些变种,但思想上都大同小异,我们需要灵活运用,发挥出最大的价值!