单例模式(Singleton Pattern)(三)

简介: 上篇文章我们讲述了,单例的推荐使用方式,以及反射对单例的破坏,但文末留了一个疑问,就是序列化如何破坏一个单例,那么本篇文章就来分析一下。

前言

iShot2022-12-05 00.30.04.png
上文我们了解常见的单例模式的创建方式,但还有没有其他的方式呢?在日常开发中推荐使用哪种呢?本文将带你深入了解其他的单例创建方式,以及单例模式的破坏。

单例模式之静态内部类


静态内部类的方式其实很简单,它就是根据静态内部类的外部类的加载不影响内部类特性,同时解决了按需加载、线程安全的问题,代码也不复杂,总结起来就是:

  • 在静态内部类里创建单例,在装载该内部类时才会去创建单例。
  • 类是由 JVM加载,而JVM只会加载1遍,保证只有1个单例,线程安全。
public class Singleton {

    /**
     * 处理器
     */
    private static class SingletonHandler {
        private static Singleton instance = new Singleton();
    }

    /**
     * 私有构造方法,无法通过new Singleton()方式创建
     */
    private Singleton() {

    }

    /**
     * 通过处理器获取实例
     */
    public static Singleton getInstance() {
        return SingletonHandler.instance;
    }
}

单例模式之枚举

这种方式好在于构造方法会被自动调用,利用这一特性也可以实现单例,默认枚举实例的创建本就是是线程安全的,即使反序列化也不会生成新的实例,任何情况下都是一个单例,满足单例模式所需的:单例创建、线程安全、代码复杂度不高,也是推荐使用的方式。
注意:暴力反射对枚举方式无效哦。

public enum Singleton {

    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

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

反射对于单例的破坏

先看回顾一下Java反射的概念,Java反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取信息、动态调用对象方法的功能就是Java的反射机制。
注意:从上述来看,这种技术过于NiuBi了,其实不仅如此,它还可以可以通过setAccessible方法(通常被称为暴力反射)来修改构造器、字段、方法的可见性。也就是说他可以对类中的私有成员进行操作,那么就破坏了我们的全局访问节点了。

public class Reflect {

    public static void main(String[] args) throws Exception {
            // 首先要获取到该类的Class 对象。
            Class<Singleton> clazz = Singleton.class;

            // getDeclaredConstructor: 不受权限控制的获取类的构造器
            Constructor c = clazz.getDeclaredConstructor(null);

            // 设置为true,就可以对类中的私有成员进行操作了
            c.setAccessible(true);

            Object instance1 = c.newInstance();
            Object instance2 = c.newInstance();

            // false
            System.out.println(instance1 == instance2);
    }
}

public class Singleton {

    private static class SingletonHandler {
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
        
    }

    public static Singleton getInstance() {
        return SingletonHandler.instance;
    }
}
那么如何解决呢?其实也很简单,只需要在单例类的构造方法中,添加判断即可。
public class Singleton {

    private static class SingletonHandler { 
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
        if(SingletonHandler.instance != null) {
            throw new RuntimeException("警告!禁止非法访问!");
        }
    }

    public static Singleton getInstance() {
        return SingletonHandler.instance;
    }
}

为什么枚举类可以阻止反射的破坏?

 
Java规范字规定,每个枚举类型及其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。在序列化的时候Java仅仅是将枚举对象的name属性输到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。
在上文中序列化的时候只将这个INSTANCE名称输出,反序列化的时候再通过这个名称,查找对应的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。
而枚举类中是没有空参构造方法的,反射方法中不予许使用反射创建枚举对象。

总结

由上文所述,静态内部类,利用 Java 的静态内部类来实现单例。这种实现方式,既支持延迟加载,也支持高并发,实现起来也比双重检测简单。而枚举方式是最简单的实现方式,基于枚举类型的单例实现。这种实现方式通过Java的枚举类型它本身的特性,保证了实例创建的线程安全性和实例的唯一性(同时阻止了反射和序列化对单例的破坏),但是破坏单例的不只有反射,想想如果是序列化的方式,有没有问题?

目录
相关文章
|
7月前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
55 1
|
7月前
|
设计模式 Java
单例模式(Singleton Pattern)
单例模式(Singleton Pattern)
51 0
|
设计模式
设计模式3 - 单例模式【Singleton Pattern】
设计模式3 - 单例模式【Singleton Pattern】
38 0
|
设计模式 安全 Java
Java单例模式(Singleton Pattern)
Java单例模式(Singleton Pattern)
|
设计模式 安全 Java
单例模式(Singleton Pattern)(一)
单例模式是Java中最简单的设计模式了,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
135 2
单例模式(Singleton Pattern)(一)
|
Java
单例模式(Singleton Pattern)(二)
上文我们了解常见的单例模式的创建方式,但还有没有其他的方式呢?在日常开发中推荐使用哪种呢?本文将带你深入了解其他的单例创建方式,以及单例模式的破坏。
164 1
单例模式(Singleton Pattern)(二)
|
设计模式 存储 缓存
详解Java设计模式之单例模式(Singleton Pattern)
详解Java设计模式之单例模式(Singleton Pattern)
241 0
详解Java设计模式之单例模式(Singleton Pattern)
|
安全 Java
创建型 - 单例模式(Singleton pattern)
创建型 - 单例模式(Singleton pattern)
创建型 - 单例模式(Singleton pattern)
|
设计模式 安全 Java
创建型模式 - 单例模式(Singleton Pattern)
创建型模式 - 单例模式(Singleton Pattern)
|
设计模式 安全 调度
【Singleton Pattern】设计模式之单例模式
【Singleton Pattern】设计模式之单例模式
166 0
【Singleton Pattern】设计模式之单例模式