文章目录
1.单例设计模式
一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
2.结构
- 单例类。只能创建一个实例的类
- 访问类。使用单例类
3.实现
3.1.饿汉式
饿汉式:类加载就会导致该单实例对象被创建
3.1.1.静态成员变量
/**
* 饿汉式:静态成员变量,在类加载时就被创建
*/
public class StaticFieldSingleton {
// 私有静态成员变量
private static StaticFieldSingleton INSTANCE = new StaticFieldSingleton();
// 私有构造方法
private StaticFieldSingleton() {}
// 提供公共方法,让外界访问该对象
public static StaticFieldSingleton getInstance() {
return INSTANCE;
}
}
3.1.2.静态代码块
/**
* 饿汉式:静态代码块,在类加载时被创建
*/
public class StaticCodeSingleton {
// 私有静态成员变量
private static StaticCodeSingleton INSTANCE;
static {
INSTANCE = new StaticCodeSingleton();
}
// 私有构造方法
private StaticCodeSingleton() {}
// 提供公共方法,让外界访问该对象
public static StaticCodeSingleton getInstance() {
return INSTANCE;
}
}
3.2.懒汉式
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
3.2.1.线程不安全
/**
* 懒汉式:线程不安全
*/
public class ThreadUnsafeSingleton {
private static ThreadUnsafeSingleton INSTANCE;
private ThreadUnsafeSingleton() {}
public static ThreadUnsafeSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new ThreadUnsafeSingleton();
}
return INSTANCE;
}
}
3.2.2.线程安全
synchronized保证线程安全,但效率低
/**
* 懒汉式:线程安全
*/
public class ThreadSafeSingleton {
private static ThreadSafeSingleton INSTANCE;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new ThreadSafeSingleton();
}
return INSTANCE;
}
}
3.2.3.双重检查
/**
* 懒汉式:双重检查(推荐)
*/
public class DoubleCheckSingleton {
/**
* volatile:防止指令重排
* new DoubleCheckSingleton()
* 1.开辟内存空间
* 2.初始化对象属性
* 3.变量指向内存地址
*
* 指令重排可能导致的执行顺序:132
* 即外界可能拿到还未初始化属性的对象,导致出现空指针异常
*/
private static volatile DoubleCheckSingleton INSTANCE;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
// 第一次判断,不为null,直接返回,无需加锁
if (INSTANCE == null) {
// 第一次判断为null,先加锁,再次判断是否为空
synchronized (DoubleCheckSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DoubleCheckSingleton();
}
}
}
return INSTANCE;
}
}
3.2.4.静态内部类
/**
* 懒汉式:静态内部类(推荐)
*/
public class InnerClassSingleton {
private InnerClassSingleton() {}
public static InnerClassSingleton getInstance() {
return InnerClassSingleton.Holder.INSTANCE;
}
/**
* 静态内部类
* JVM在加载外部类时,不会加载静态内部类
* 只有在调用静态内部类的方法、属性时,才会加载
*/
static class Holder {
private static InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
}
3.3.枚举
public enum EnumSingleton {
INSTANCE;
EnumSingleton() {
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
4.破坏单例模式
4.1.序列化破坏单例
对象写到文件中
public static void writeObj2File() throws Exception{
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/pattern/creator/singleton/breaking/a.txt"));
oos.writeObject(instance);
oos.close();
}
从文件中读取对象
public static Singleton readObjFromFile() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/pattern/creator/singleton/breaking/a.txt"));
Singleton instance = (Singleton) ois.readObject();
System.out.println(instance);
ois.close();
return instance;
}
多次读取得到的不是同一个对象
解决方法:readResolve()
// 进行反序列化时,会自动调用该方法
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
4.2.反射破坏单例
破坏非枚举的单例
@Test
public void reflect1() throws Exception{
// 反射可以破坏除了枚举类的所有单例模式
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance1 = constructor.newInstance();
Singleton instance2 = constructor.newInstance();
System.out.println(instance1 == instance2);
}
破坏枚举类失败
@Test
public void reflect2() throws Exception {
// 反射破坏枚举失败
// NoSuchMethodException: pattern.creator.singleton.villain.EnumSingleton.<init>()
Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
EnumSingleton instance1 = constructor.newInstance();
EnumSingleton instance2 = constructor.newInstance();
System.out.println(instance1 == instance2);
}
@Test
public void reflect3() throws Exception {
// 反射破坏枚举失败
// IllegalArgumentException: Cannot reflectively create enum objects
Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnumSingleton instance1 = constructor.newInstance("a", 1);
EnumSingleton instance2 = constructor.newInstance("b", 2);
System.out.println(instance1 == instance2);
}
5.JDK中的应用
Runtime
public class Runtime {
// 静态成员变量:在类加载时,创建对象
private static Runtime currentRuntime = new Runtime();
/**
* 提供公共方法,让外界访问对象
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** 构造器私有化 */
private Runtime() {}