设计模式-单例模式
一、定义
确保某个类只有一个实例,并向整个系统提供出这个实例。
二、类型
创建型
三、适用场景
想确保任何情况下都只有一个实例
四、优点
- 内存只有一个实例,节省内存。
- 避免对资源多重占用。
- 在系统设置全局访问点,优化和共享资源访问。
五、缺点
- 一般没有接口,扩展困难。
- 与单一职责有冲突。
六、类图
过于简单,略
七、代码实现
1、懒汉式:
package com.wms.createtype.singleton;
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton() {}
public static LazySingleton getInstance() {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
return lazySingleton;
}
}
2、双重检查式:
package com.wms.createtype.singleton;
public class DoubleCheckSingleton {
// volatile 禁止指令重排
private volatile static DoubleCheckSingleton doubleCheckSingleton = null;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
if (doubleCheckSingleton == null) {
synchronized (DoubleCheckSingleton.class) {
if (doubleCheckSingleton == null) {
// 1、分配内存给对象
// 2、初始化对象
// 3、赋值给doubleCheckSingleton
// jvm的指令重排可能会使得2和3这两步顺序不确定
// 当执行顺序是1->3->2的时候,就会出现问题,此时doubleCheckSingleton不为空,别的线程来获取这个单例对象
// 时就可能拿到还没初始化的这个对象,此时如果别的线程直接使用,就会出错。因此加上volatile禁止jvm指令重排
doubleCheckSingleton = new DoubleCheckSingleton();
}
}
}
return doubleCheckSingleton;
}
}
3、饿汉式:
package com.wms.createtype.singleton;
public class HungarySingleton {
private static HungarySingleton hungarySingleton = new HungarySingleton();
private HungarySingleton() {}
public static HungarySingleton getInstance() {
return hungarySingleton;
}
}
4、静态内部内:
package com.wms.createtype.singleton;
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {}
public static StaticInnerClassSingleton getInstance() {
return InnerClass.staticInnerClassSingleton;
}
private static class InnerClass {
private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
}
5、枚举单例
package com.wms.createtype.singleton;
public enum EnumSingleton {
INSTANCE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
八、破坏单例
1、反序列化破坏单例
测试函数:
package com.wms.createtype.singleton;
import java.io.*;
public class TestDesignParttern {
public static void main(String[] args) throws IOException, ClassNotFoundException {
HungarySingleton hungarySingleton = HungarySingleton.getInstance();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("singletion"));
objectOutputStream.writeObject(hungarySingleton);
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("singletion"));
HungarySingleton hungarySingleton01 = (HungarySingleton) objectInputStream.readObject();
System.out.println(hungarySingleton == hungarySingleton01); // false
}
}
修改单例类:
package com.wms.createtype.singleton;
import java.io.Serializable;
public class HungarySingleton implements Serializable {
private static HungarySingleton hungarySingleton = new HungarySingleton();
private HungarySingleton() {}
public static HungarySingleton getInstance() {
return hungarySingleton;
}
// 加上该方法在反序列化的时候返回已存在的单例对象
public Object readResolve() {
return hungarySingleton;
}
}
防御方式:单例模式中增加反序列化方法,该方法隐式调用。
2、反射破坏单例
网上有些通过构造方法防御:
package com.wms.createtype.singleton;
import java.io.Serializable;
public class HungarySingleton implements Serializable {
private static HungarySingleton hungarySingleton = new HungarySingleton();
private HungarySingleton() {
if (hungarySingleton != null) {
throw new RuntimeException("单例模式不允许调用构造方法实例化");
}
}
public static HungarySingleton getInstance() {
return hungarySingleton;
}
// 加上该方法在反序列化的时候返回已存在的单例对象
public Object readResolve() {
return hungarySingleton;
}
}
破坏方式:
package com.wms.createtype.singleton; import java.io.Serializable; public class HungarySingleton implements Serializable { private static final HungarySingleton hungarySingleton = new HungarySingleton(); private HungarySingleton() { if (hungarySingleton != null) { throw new RuntimeException("单例模式不允许调用构造方法实例化"); } } public static HungarySingleton getInstance() { return hungarySingleton; } // 加上该方法在反序列化的时候返回已存在的单例对象 public Object readResolve() { return hungarySingleton; } }
public class StaticInnerClassSingleton { private StaticInnerClassSingleton() { if (InnerClass.staticInnerClassSingleton != null) { throw new RuntimeException("单例模式不允许调用构造方法实例化"); } } public static StaticInnerClassSingleton getInstance() { return InnerClass.staticInnerClassSingleton; } private static class InnerClass { private static final StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); } }
防御方式:饿汉式和静态内部类,增加final修饰,可防止反射攻击,其他都无法防御。
3、防御任何破坏单例的方式:Effective Java推荐尽可能地使用枚举来实现单例