【设计模式|创建型】单例模式 饿汉、懒汉、静态内部类、枚举、破坏单例与防破坏单例

详解

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

  • 提供唯一的实例,避免产生多个实例造成资源浪费。
  • 全局访问点使得其他对象可以方便地访问该实例。

饿汉式(Eager Initialization)

饿汉式是在类加载时就创建实例,并始终保持单例。

示例代码如下:

public class EagerSingleton {
    // 在类加载时就创建实例
    private static final EagerSingleton instance = new EagerSingleton();

    // 构造函数私有化,避免通过new创建实例
    private EagerSingleton() {
    }

    // 获取实例的方法
    public static EagerSingleton getInstance() {
        return instance;
    }
}

懒汉式(Lazy Initialization)

懒汉式是在第一次使用时才创建实例,避免不必要的资源消耗。需要注意线程安全问题。

示例代码如下:

public class LazySingleton {
    private static volatile LazySingleton instance;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (instance == null) {// 第一次检查,确保性能,如果已创建,不走同步块
            synchronized (LazySingleton.class) {
                if (instance == null) {//第二次检查,确保单例
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

静态内部类(Static Inner Class)

静态内部类是一种延迟加载的方式,并且保证了线程安全性。

示例代码如下:

public class StaticInnerSingleton {
    private StaticInnerSingleton() {
    }

    // 静态内部类,用于延迟加载
    private static class SingletonHolder {
        private static final StaticInnerSingleton instance = new StaticInnerSingleton();
    }

    public static StaticInnerSingleton getInstance() {
        return SingletonHolder.instance;
    }
}

枚举(Enum)

枚举方式是Java中最简洁、安全的单例模式实现方式,可以避免通过反射和反序列化破坏单例。

示例代码如下:

public enum EnumSingleton {
    INSTANCE;

    // 添加其他方法和属性
    public void doSomething() {
        // 执行操作
    }
}

破坏单例模式

单例模式可以通过反射和反序列化来破坏。下面是详细解释:

  • 反射破坏单例模式:通过使用反射,可以访问私有构造方法,并强制创建多个实例。

示例代码如下:

public class ReflectionSingletonTest {
    public static void main(String[] args) {
        EagerSingleton instance1 = EagerSingleton.getInstance();

        EagerSingleton instance2 = null;
        try {
            Constructor<EagerSingleton> constructor = EagerSingleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            instance2 = constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("Instance 1 hashCode: " + instance1.hashCode());
        System.out.println("Instance 2 hashCode: " + instance2.hashCode());
    }
}

输出结果:

Instance 1 hashCode: 366712642

Instance 2 hashCode: 1829164700

如上所示,通过反射可以创建出多个实例,破坏了单例模式。

  • 反序列化破坏单例模式:当一个单例类被序列化后,再次进行反序列化时,会创建新的实例。

示例代码如下:

public class SerializedSingletonTest {
    public static void main(String[] args) {
        SerializedSingleton instance1 = SerializedSingleton.getInstance();

        try {
            // 将实例进行序列化
            FileOutputStream fileOut = new FileOutputStream("singleton.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(instance1);
            out.close();
            fileOut.close();
            
            // 将序列化后的实例进行反序列化
            FileInputStream fileIn = new FileInputStream("singleton.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            SerializedSingleton instance2 = (SerializedSingleton) in.readObject();
            in.close();
            fileIn.close();

            System.out.println("Instance 1 hashCode: " + instance1.hashCode());
            System.out.println("Instance 2 hashCode: " + instance2.hashCode());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 输出结果:

Instance 1 hashCode: 1216424564

Instance 2 hashCode: 1208627083

如上所示,通过反序列化可以创建出多个实例,破坏了单例模式。

防止破坏单例模式

为了避免反射和反序列化对单例模式的破坏,可以采取以下措施:

  1. 私有化构造方法:将单例类的构造方法设置为私有,防止外部类通过new关键字创建新的实例。

  2. 防止反射破坏:可以在单例类的私有构造方法中添加逻辑,当检测到已存在实例时,抛出异常,防止通过反射创建新的实例。也可以在单例类中添加一个标志位,当已存在实例时,再次调用构造方法时直接返回已有实例。

  3. 防止反序列化破坏:可以在单例类中实现 readResolve() 方法,返回已有实例,防止通过反序列化创建新的实例。

下面是一个综合应用了上述措施的单例模式示例代码:

public class Singleton implements Serializable {
    private static final long serialVersionUID = 1L;

    private static volatile Singleton instance;

    private Singleton() {
        // 防止通过反射创建新的实例
        if (instance != null) {
            throw new RuntimeException("Cannot create new instance, please use getInstance() method.");
        }
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值