设计模式之单例模式(Java)

单例模式实现方式:懒汉式、饿汉式、双重检查、枚举、静态内部类;

懒汉式:

/**
 * 懒汉式单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class LazySingleton implements Serializable {
    private static LazySingleton lazySingleton=null;
    private LazySingleton(){

    }
    //特点:第一次调用才初始化,避免内存浪费。
    public synchronized static LazySingleton getInstance(){
        if(lazySingleton==null){
            lazySingleton=new LazySingleton();
        }
        return lazySingleton;
    }
}

饿汉式:

java 复制代码
/**饿汉式单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class HungrySingleton {
    static final HungrySingleton hungrySingleton=new HungrySingleton();
    private HungrySingleton(){

    }
    //特点:类加载时就初始化,线程安全
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

双重检查:

和懒汉式的区别就是,锁的范围减小了,防止多线程场景下创建多个实例,所以又加了一层判断。

java 复制代码
/**双重检查单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class DoubleCheckSingletion {
    private static DoubleCheckSingletion doubleCheckSingletion=null;
    private DoubleCheckSingletion(){

    }
    //特点:安全且在多线程情况下能保持高性能
    public static DoubleCheckSingletion getInstance(){
        if(doubleCheckSingletion==null){
            synchronized (DoubleCheckSingletion.class){
                if(doubleCheckSingletion==null){
                    doubleCheckSingletion=new DoubleCheckSingletion();
                }
            }
        }
        return doubleCheckSingletion;
    }
}

枚举:

java 复制代码
/**枚举形单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public enum EnumSingleton {
    INSTANCE;
    //特点:自动支持序列化机制,绝对防止多次实例化
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

静态内部类:

java 复制代码
/**静态内部类单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class StaticInnerClassSingleton {
    private static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton();
    }
    private StaticInnerClassSingleton(){

    }

    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }
}

序列化或反射破坏单列模式:

通过序列化反序列化或反射可以破坏除枚举以外的其它实现方式:

java 复制代码
LazySingleton instance = LazySingleton.getInstance();
        //序列化破坏单例模式
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singletion"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singletion"));
        LazySingleton instance2 = (LazySingleton) ois.readObject();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance2==instance);

运行:

解决反序列化破坏单列模式

解决方法只需要在单例类里加上一个readResolve()方法即可,原因就是在反序列化的过程中,会检测readResolve()方法是否存在,如果存在的话就会反射调用readResolve()这个方法。

java 复制代码
private Object readResolve(){
        return lazySingleton;
    }

使用枚举单例模式天然就可以防止序列化破坏单例模式:

java 复制代码
 EnumSingleton instance = EnumSingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singletion"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singletion"));
        EnumSingleton instance2 = (EnumSingleton) ois.readObject();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance==instance2);

运行:

为什么?

  • 枚举类的序列化保证单例: Java枚举类型在序列化和反序列化时会被特殊处理。在序列化的过程中,只是将枚举对象的名字(即枚举常量的名字)写入到序列化文件中;在反序列化的过程中,通过名字来获取枚举对象。这保证了在反序列化过程中无论如何都只会得到枚举中定义的枚举常量,而不会重新创建新的对象。因此,枚举类型的单例模式在反序列化过程中也能保持单例的状态,不会被破坏。

  • 禁止反射创建多个实例 : 枚举类型的单例模式天然地禁止了通过反射机制来创建多个实例。枚举类型的构造器是私有的,并且编译器会确保枚举常量只能被实例化一次。如果尝试使用反射来调用枚举类型的私有构造器来创建新的实例,会抛出IllegalArgumentException异常,从而保证了单例的唯一性。

反射破坏单例模式:

java 复制代码
/**反射破坏单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class BrokenSingletonTest2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        LazySingleton instance = LazySingleton.getInstance();
        Class<LazySingleton> lazySingletonClass = LazySingleton.class;
        Constructor<LazySingleton> constructor = lazySingletonClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazySingleton refInstance = constructor.newInstance();
        System.out.println(instance);
        System.out.println(refInstance);
        System.out.println(instance==refInstance);
    }
}

运行:

枚举天然也可以防止反射破坏单例模式:

java 复制代码
        EnumSingleton instance = EnumSingleton.getInstance();
        Class<EnumSingleton> singletonClass = EnumSingleton.class;
        Constructor<EnumSingleton> constructor = singletonClass.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumSingleton refInstance = constructor.newInstance();
        System.out.println(instance);
        System.out.println(refInstance);
        System.out.println(instance==refInstance);

运行:

这也是为什么枚举是实现单例模式最好的方式原因之一。

相关推荐
试行9 分钟前
Android实现自定义下拉列表绑定数据
android·java
茜茜西西CeCe15 分钟前
移动技术开发:简单计算器界面
java·gitee·安卓·android-studio·移动技术开发·原生安卓开发
救救孩子把20 分钟前
Java基础之IO流
java·开发语言
小菜yh21 分钟前
关于Redis
java·数据库·spring boot·redis·spring·缓存
宇卿.27 分钟前
Java键盘输入语句
java·开发语言
浅念同学28 分钟前
算法.图论-并查集上
java·算法·图论
立志成为coding大牛的菜鸟.41 分钟前
力扣1143-最长公共子序列(Java详细题解)
java·算法·leetcode
鱼跃鹰飞41 分钟前
Leetcode面试经典150题-130.被围绕的区域
java·算法·leetcode·面试·职场和发展·深度优先
仙魁XAN1 小时前
Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
unity·设计模式·工厂方法模式·抽象工厂模式
爱上语文2 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring