zoukankan      html  css  js  c++  java
  • 个人对单例模式的理解

    设计模式GOF23:

    创建型模式:

      单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式

    结构型模式:

      适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

    行为型模式:

      命令模式、模板方法模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

    单例模式核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

    单例模式应用:如Windows系统中的任务管理器和回收站,永远只存在一个。JavaWeb中的Servlet也是单例......

    单例模式的优点:①只生成一个实例,减少了系统的性能开销。当一个对象的产生需要较多的性能资源时,比如读取配置文件或者依赖于其他对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存来解决。②单例模式可以在系统设置永久的访问点,优化共享资源的访问。

    常见的五种单例模式实现方法:

      一、主要:

        * 饿汉式(线程安全,调用效率高,不能延迟加载)

        * 懒汉式(线程安全,调用效率不高,能够延迟加载)

      二、其他:

        * 双重检测锁机制(由于JVM的内存模型原因,偶尔会出错)

        * 静态内部类机制(线程安全,调用效率高,可以延迟加载)

        * 枚举(枚举是天然的单例模式,线程安全,调用效率高,不能延迟加载)

    代码实现:

    1、饿汉式

    import java.io.ObjectStreamException;
    import java.io.Serializable;
    
    /**
     * Title: SingletonDemo01类<br>
     * Description: 单例模式-饿汉式
     * Company: 卓瀛工作室
     *
     * @author 杨万浩
     * @version 1.0
     */
    public class SingletonDemo01 {
    
        /*
         * 类初始化时,立即加载这个对象
         * 由于对象是初始化时就创建好的,所以不管几个线程访问的都是这个对象
         */
        private static SingletonDemo01 instance = new SingletonDemo01();
    
        //私有的构造方法
        private SingletonDemo01(){
           
        }
    
        /*
         * 线程安全的,即使没有synchronized修饰也是线程安全的
         * 由于不需要synchronized修饰,方法没有同步锁,所以效率高
         */
        public static SingletonDemo01 getInstance() {
            return instance;
        }
    }

    2、懒汉式

    /**
     * Title: SingletonDemo02类<br>
     * Description: 单例模式-懒汉式
     * Company: 卓瀛工作室
     *
     * @author 杨万浩
     * @version 1.0
     */
    public class SingletonDemo02 {
    
        // 类初始化时,不会初始化这个对象,真正使用时再创建
        private static SingletonDemo02 instance;
    
        // 私有的构造方法
        private SingletonDemo02 (){}
    
        /*
         * 可以懒加载,真正需要这个对象时再创建这个对象
         * 为了线程安全,需要加synchronized同步锁,也会因此而效率低
         */
        public static synchronized SingletonDemo02 getInstance() {
            if( instance == null ) {
                instance = new SingletonDemo02();
            }
            return instance;
        }
    }

    3、双重检测锁机制

    /**
     * Title: SingletonDemo03类<br>
     * Description: 单例模式-双重检测锁式
     * Company: 卓瀛工作室
     *
     * @author 杨万浩
     * @version 1.0
     */
    public class SingletonDemo03 {
    
        private static SingletonDemo03 instance;
    
        private SingletonDemo03(){}
    
        private static SingletonDemo03 getInstance() {
            if( instance == null ) {
                SingletonDemo03 sc;
                synchronized (SingletonDemo03.class) {
                    sc = instance;
                    if( sc == null ) {
                        synchronized (SingletonDemo03.class) {
                            sc = new SingletonDemo03();
                        }
                        instance = sc;
                    }
                }
            }
            return instance;
        }
    }

    4、静态内部类实现

    /**
     * Title: SingletonDemo04类<br>
     * Description: 单例模式-静态内部类实现
     * Company: 卓瀛工作室
     *
     * @author 杨万浩
     * @version 1.0
     */
    public class SingletonDemo04 {
    
        private static class SingletonClassInstance{
            private static final SingletonDemo04 instance = new SingletonDemo04();
        }
        private SingletonDemo04() {}
    
        public static SingletonDemo04 getInstance() {
            return SingletonClassInstance.instance;
        }
    }

    5、枚举

    /**
     * Title: SingletonDemo05类<br>
     * Description: 单例模式-枚举方式
     * Company: 卓瀛工作室
     *
     * @author 杨万浩
     * @version 1.0
     */
    public enum  SingletonDemo05 {
    
        STUDENT1(21,"张三"),STUDENT2(20,"李四"),STUDENT3(22,"王五");
        
        int age;
        String name;
        
        private SingletonDemo05(int age, String name) {
            this.age = age;
            this.name = name;
        }
    }

    但是,上边五种方法,除了最后一种(枚举实现)之外,其他四种都会存在两个漏洞。

    1、反射漏洞

      示例代码:

    //通过正规途径获取到的对象s1和s2
    SingletonDemo01 s1 = SingletonDemo01.getInstance();
    SingletonDemo01 s2 = SingletonDemo01.getInstance();
    //通过反射获取到的对象s3
    Class clazz = Class.forName("cn.yangwanhao.singleton.SingletonDemo01");
    Constructor constructor = clazz.getDeclaredConstructor();
    constructor.setAccessible(true);
    SingletonDemo01 s3 = (SingletonDemo01) constructor.newInstance();
    //输出这三个对象的地址
    System.err.println(s1 + " : s1");
    System.err.println(s2 + " : s2");
    System.err.println(s3 + " : s3");

      输出结果:

    cn.yangwanhao.singleton.SingletonDemo01@50134894 : s1
    cn.yangwanhao.singleton.SingletonDemo01@50134894 : s2
    cn.yangwanhao.singleton.SingletonDemo01@5fdef03a : s3

    由输出结果可以看到,s1和s2是同一个对象,而s3就是另外一个对象了。

      修补方法:在私有的构造方法里加上一个if判断,再次通过上述反射代码获取对象时会报异常

    //私有的构造方法
    private SingletonDemo01(){
        if( instance != null ) {
            throw new RuntimeException();
        }
    }

    2、反序列化漏洞

      示例代码:

    //通过正规途径获取到的对象s1和s2
    SingletonDemo01 s1 = SingletonDemo01.getInstance();
    SingletonDemo01 s2 = SingletonDemo01.getInstance();
    //通过将s1序列化到磁盘再反序列化得到的s4
    FileOutputStream ops = new FileOutputStream("d:/a.txt");
    ObjectOutputStream oops = new ObjectOutputStream(ops);
    oops.writeObject(s1);
    ops.close();
    oops.close();
    FileInputStream ois = new FileInputStream("d:/a.txt");
    ObjectInputStream oips = new ObjectInputStream(ois);
    SingletonDemo01 s4 = (SingletonDemo01) oips.readObject();
    System.err.println(s1 + " : s1");
    System.err.println(s2 + " : s2");
    System.err.println(s4 + " : s4");

      输出结果:

    cn.yangwanhao.singleton.SingletonDemo01@50134894 : s1
    cn.yangwanhao.singleton.SingletonDemo01@50134894 : s2
    cn.yangwanhao.singleton.SingletonDemo01@5fdef03a : s4

      结论同上

      修补方法:在单例类中加入以下方法代码

    private Object readResolve() throws ObjectStreamException {
        return instance;
    }
  • 相关阅读:
    端口服务
    系统设计的主要原则是什么?
    Dynamics CRM2015 Custom Code Validation Tool工具的使用
    CONFIGURE ADFS 3.0 WITH SHAREPOINT 2013
    Sharepoint Solution Gallery Active Solution时激活按钮灰色不可用的解决方法
    Dynamics CRM 2015Online Update1 new feature之 插件跟踪日志
    Dynamics CRM2013/2015 Plugin注册工具Register New Assembly时无法看到注册按钮的解决办法
    Dynamics CRM 2015 站点地图公告配置实体显示名称的变更
    Dynamics CRM 2015 Online Update1 UI界面的更新变化
    SQL Server2012 AlwaysOn 无法将数据库联接到可用性组 针对主副本的连接未处于活动状态
  • 原文地址:https://www.cnblogs.com/yangwanhao/p/11297204.html
Copyright © 2011-2022 走看看