文章背景
单例模式自学笔记, 自学java到了SHH框架阶段, 想看源码却看不懂, 恶补一下设计模式.
Singleton Pattern 单例模式
概念:
1.是一种用来创建对象的模式.
2.一个类仅有一个实例, 并且提供一个访问它的全局访问点.
使用范围
在创建对象时, 如果需要创建的对象非常复杂或者占用内存非常大, 一般采用此模式.
好处:
1.因为对象复杂可以简化创建细节, 使用者更方便的创建该类.
2.保证只有一个实例, 减少内存开销, 并且可以控制类的创建时刻.
实现方法
常见对象的创建方式
new
反射(reflect)
反序列化(deserialize)
克隆(clone)
公开的静态变量
公开的静态方法
实现类型
| 立即or延迟 | 线程安全 | 效率 | 实现难度 | 自动处理序列化问题
——— | ————- | ———— | ———— | ———- | —
饿汉式 | 立即 | 安全 | | 简单 |
懒汉式 | 延迟 | 不安全 | | 简单 |
加锁懒汉式 | 延迟 | 安全 | 低 | 中等 |
双重验证式懒汉式 | 延迟 | 安全 | | 复杂 |
类加载方式 | 延迟 | 安全 | | 简单 |
枚举实现 | 立即| 安全 | | 简单 | 能
饿汉式:
public class Dog {
// 创建唯一的实例,在类加载时即被创建不能做到延迟创建
private static final Dog ONLYDOG = new Dog();
// 私有化构造器,防止被外部new
private Dog() {}
// 创建全局访问点,中间可以添加其他代码比公开静态变量的方式要更灵活
public static Dog getDog(){
//此处可以添加控制代码
return ONLYDOG;
}
//可以写个测试类,结果为true
public class Test {
public static void main(String[] args) {
Dog a = Dog.getDog();
Dog b = Dog.getDog();
System.out.println(a == b);
}
}
懒汉式&加锁懒汉式
public class Cat {
// 唯一实例变量
private static Cat ONLYCAT;
// 屏蔽外部new
private Cat(){
}
// 提供全局访问点
public static Cat getCat(){ //未加锁,线程不安全
// public synchronized static Dog getDog(){ //加锁,需要排队等待
if (ONLYCAT != null) {
ONLYCAT = new Cat();
}
return ONLYCAT;
}
}
双重验证式
public class Cat {
// 创建唯一实例
private static Cat ONLYCAT;
// 屏蔽外部new
private Cat(){
}
// 提供全局访问点
public static Cat getCat(){
//...
if (ONLYCAT != null) {
synchronized (Cat.class) {//只对new的部分加锁,并且在此验证,线程安全并且效率较高.
if (ONLYCAT != null) {
ONLYCAT = new Cat();
}
}
}
return ONLYCAT;
}
}
内部类加载
涉及到序列化相关内容参考博客
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Cat implements Serializable, Cloneable{
// 屏蔽外部new
private Cat(){
}
// 静态内部类,加载内部类的时候jvm会保证线程安全
private static class InstanceHolder{
static private Cat ONLYCAT = new Cat();
}
// 提供全局访问点
public static Cat getCat(){
return InstanceHolder.ONLYCAT;
}
// 如果必须实现序列化和克隆,需要如下代码防止反序列化和克隆来新建实例
// serializable通过readResolve方法解决,clone通过覆盖clone方法解决
private Object readResolve() throws ObjectStreamException{
return getCat();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return getCat();
}
}
枚举方式实现
public enum Tiger {\\相当于 public class Tiger extends Enum{
ONLYTIGER;\\单实例
private String a = "I am ";\\枚举中也可以定义变量和方法
private String b = "the ONLYTIGER!";
public String whoAreYou(){
return a+b;
}
}
测试
public class Test {
public static void main(String[] args) {
System.out.println(Tiger.ONLYTIGER.whoAreYou());
Tiger a = Tiger.ONLYTIGER;
Tiger b = Tiger.ONLYTIGER;
System.out.println(a == b);
}
}
测试结果
I am the ONLYTIGER!
true
总结
饿汉式:仅一个缺点,没有延迟加载的需求都可以使用
懒汉式&加锁懒汉式: 有双重验证和内部类两种更好的方式,基本用不到
双重验证: 实现的过程中控制比较多,有时能巧妙的实现特殊需求
内部类: 需要延迟加载的第一选择, 无法满足需求时可以考虑双重验证
枚举: 很多文章说是目前最好的单例模式实现. 对此有一些质疑, 枚举毕竟不能延迟加载,我认为最佳应用应该是饿汉式的替代, 比饿汉式更加优雅.