设计模式之单例模式


简介

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

一、单例模式实现方式

1、懒汉式

public class LazySingleton {

	// 因为new的过程会进行指令重排,需要使用保证可见性,所以使用volatile 
    private static volatile LazySingleton lazySingleton = null;

    public static LazySingleton getLazySingleton(){
    	// volatile 保证此处的可见性
        if (lazySingleton != null){
            return lazySingleton;
        }
        synchronized (LazySingleton.class){
            if (lazySingleton == null){
                lazySingleton = new LazySingleton();
            }
            return lazySingleton;
        }
    }

}

2、饿汉式

public class HungrySingleton {

    private static volatile HungrySingleton hungrySingleton = new HungrySingleton();

    private static HungrySingleton getHungrySingleton(){
            return hungrySingleton;
    }

}

因为饿汉式的类加载时候就会初始化,为了防止该单例不适用而导致资源浪费,可以使用静态内部类进行优化

public class LazySingleton {

    // 防止被重写
    // 调用getHungrySingleton()方法,则会先加载静态内部类(类加载机制)
    private static final LazySingleton getHungrySingleton() {
        return UpgradeHungrySingleton.hungrySingleton;
    }

    // 类加载,不加载内部类
    private static final class UpgradeHungrySingleton {
        private static final LazySingleton hungrySingleton = new LazySingleton();
    }

}

3、枚举

使用枚举类实现单例模式是最简洁和安全的方式,因为枚举类型本身是线程安全的,并且只会实例化一次,并且无法被破坏

public enum Singleton {
    INSTANCE;

    // 可以添加其他方法
    public void someMethod() {
        // 方法实现
    }
}

二、单例模式的失效场景

1、反射破坏单例

反射可以绕过构造方法的私有访问控制,从而再次创建实例。

import java.lang.reflect.Constructor;

public class SingletonReflection {
    public static void main(String[] args) {
        try {
            Singleton instance1 = Singleton.getInstance();
            Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton instance2 = constructor.newInstance();
            // 输出 false,说明创建了两个实例
            System.out.println(instance1 == instance2); 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        // 私有构造方法防止实例化
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

2、序列化破坏单例

序列化和反序列化可能导致创建多个实例,即使类是单例的

import java.io.*;

public class SingletonSerialization {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();

        // 序列化
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
        out.writeObject(instance1);
        out.close();

        // 反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
        Singleton instance2 = (Singleton) in.readObject();
        in.close();
		// 输出 false,说明创建了两个实例
        System.out.println(instance1 == instance2); 
    }
}

class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() { 
        // 私有构造方法防止实例化 
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

防止序列化破坏
调整单例类,添加 readResolve 方法,可以避免上述问题

class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() { 
        // 私有构造方法防止实例化 
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    // 防止反序列化时创建新的实例
    protected Object readResolve() {
        return INSTANCE;
    }
}

3、多类加载器破坏单例

如果应用程序使用了多个类加载器加载同一个类,不同类加载器可能会产生不同的类实例。

ClassLoader classLoader1 = new CustomClassLoader();
ClassLoader classLoader2 = new CustomClassLoader();
Class<?> singletonClass1 = classLoader1.loadClass("Singleton");
Class<?> singletonClass2 = classLoader2.loadClass("Singleton");
Object instance1 = singletonClass1.getMethod("getInstance").invoke(null);
Object instance2 = singletonClass2.getMethod("getInstance").invoke(null);
// 输出 false,说明创建了两个实例
System.out.println(instance1 == instance2);

4、克隆破坏单例

如果 Singleton 类实现了 Cloneable 接口并重写 clone() 方法

class Singleton implements Cloneable {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() { 
        // 私有构造方法防止实例化 
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    	// 可能会生成新的实例
        return super.clone(); 
    }
}

public class SingletonClone {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = (Singleton) instance1.clone();
        // 输出 false,说明创建了两个实例
        System.out.println(instance1 == instance2); 
    }
}

防止克隆破坏
重写 clone() 方法,直接返回单例实例

class Singleton implements Cloneable {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() { 
        // 私有构造方法防止实例化 
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 防止克隆创建新实例
        return INSTANCE;
    }
}

小知识
枚举在 JVM 层面确实提供了很强的保证,使其成为实现单例模式时几乎不会被破坏的选择

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值