美文网首页
Java单例模式的实现与破坏

Java单例模式的实现与破坏

作者: 与李_han | 来源:发表于2020-08-20 13:03 被阅读0次

    单例模式是一种设计模式,是在整个运行过程中只需要产生一个实例。那么怎样去创建呢,以下提供了几种方案。

    一、创建单例对象

    懒汉式

    public class TestSingleton {
    
        // 构造方法私有化
        private TestSingleton(){}
    
        // 声明实例
        private static TestSingleton singleton;
    
        // 提供外部调用方法,生成并获取实例
        public static TestSingleton getInstance() {
            if(singleton == null) {
                singleton = new TestSingleton();
            }
            return singleton;
        }
    }
    

    此方案是以时间换空间,启动时并不会执行任何操作,只有被调用时,采取实例化对象。不过这种方法在多线程下不安全,因为两个线程如果同时调用时,会同时通过非空验证的验证,造成创建两个对象的后果,有悖设计初衷。

    针对多线程问题,应该加入双重非空判断:

    public class TestSingleton {
    
        // 构造方法私有化
        private TestSingleton(){}
    
        // 声明实例
        private static volatile TestSingleton singleton;
    
        // 提供外部调用方法,生成并获取实例
        public static TestSingleton getInstance() {
            if(singleton == null) {
                synchronized (TestSingleton.class) {
                    if(singleton == null) {
                        singleton = new TestSingleton();
                    }
                }
            }
            return singleton;
        }
    }
    

    饿汉式

    public class TestSingleton {
    
        // 构造方法私有化
        private TestSingleton(){}
    
        // 声明并生成实例
        private static TestSingleton singleton = new TestSingleton();
    
        // 提供外部调用方法,获取实例
        public static TestSingleton getInstance() {
            return singleton;
        }
    }
    
    

    以空间换时间,类一加载时,就对其进行实例化,后面调用时直接提供对象实例。

    静态内部类实现懒加载

    public class TestSingleton {
    
        // 构造方法私有化
        private TestSingleton(){}
    
        private static class TestSingletonFactory{
            private static TestSingleton singleton = new TestSingleton();
        }
    
        // 提供外部调用方法,获取实例
        public static TestSingleton getInstance() {
            return TestSingletonFactory.singleton;
        }
    }
    

    当getInstance方法被调用时,才会初始化静态内部类TestSingletonFactory的静态变量singleton。此处由JVM来保障线程安全。

    二、破坏单例

    实现单例后,按照预期结果应该所有对象都是同一个对象。但是以下有几种情况可以破坏单例的性质。

    首先让单例类实现Serializable, Cloneable接口,以便实验。

    public class TestSingleton implements Serializable, Cloneable{
    
        private static final long serialVersionUID = 1L;
    
        // 构造方法私有化
        private TestSingleton(){}
    
        private static class TestSingletonFactory{
            private static TestSingleton singleton = new TestSingleton();
        }
    
        // 提供外部调用方法,获取实例
        public static TestSingleton getInstance() {
            return TestSingletonFactory.singleton;
        }
    }
    
    • 序列化
    // 获取实例
    TestSingleton originSingleton = TestSingleton.getInstance();
    // 写出对象
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(originSingleton);
    // 写入对象
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    TestSingleton serializeSingleton = (TestSingleton) ois.readObject();
    // 判断两个对象是否相等
    System.out.println(originSingleton == serializeSingleton); // false
    
    • 反射
    // 反射
    Class<TestSingleton> clazz = TestSingleton.class;
    // 获取无参构造函数
    Constructor<TestSingleton> constructor = clazz.getDeclaredConstructor();
    // 将私有设置为可见
    constructor.setAccessible(true);
    // 用构造器生成实例
    TestSingleton instance = constructor.newInstance();
    // 判断两个对象是否相等
    System.out.println(originSingleton == instance); // false
    
    • 克隆
    // 克隆
    TestSingleton clone = (TestSingleton) originSingleton.clone();
    System.out.println(originSingleton == clone); // false
    

    三、修复破坏

    对于这种预料之外的结果,我们应该怎样去控制呢?

    • 序列化

    添加readResolve方法,返回Object。

    • 反射

    添加全局可见变量,如果再次调用构造方法生成实例时,抛出运行时错误。

    • 克隆

    重写clone方法,直接返回单例对象。

    public class TestSingleton implements Serializable, Cloneable{
    
        private static final long serialVersionUID = 1L;
    
        private static volatile boolean isCreated = false;//默认是第一次创建
    
        // 构造方法私有化
        private TestSingleton(){
            if(isCreated) {
                throw new RuntimeException("实例已经被创建");
            }
            isCreated = true;
        }
    
        private static class TestSingletonFactory{
            private static TestSingleton singleton = new TestSingleton();
        }
    
        // 提供外部调用方法,获取实例
        public static TestSingleton getInstance() {
            return TestSingletonFactory.singleton;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return getInstance();
        }
    
        /**
         * 防止序列化破环
         * @return
         */
        private Object readResolve() {
                return getInstance();
            }
    }
    

    相关文章

      网友评论

          本文标题:Java单例模式的实现与破坏

          本文链接:https://www.haomeiwen.com/subject/dzpsjktx.html