• 设计模式-单例模式(Singleton Pattern)


    一、概念

    1.1 定义

    它是一种创建类的对象的模式,能够确保系统中只产生该类的一个对象。

    1.2 作用

    1. 可以省略那些被频繁使用的对象的创建时间,节省系统开销。
    2. 降低内存使用频率,减轻GC压力,缩短GC停顿时间。

    二、种类

    2.1 饿汉式单例

    public class Singleton1 {
        /**
         * 必须有一个private修饰的构造器
         */
        private Singleton1() {
            System.out.println("Singleton instance is create!!");
        }
    
        //该成员变量必须用static修饰
        private static Singleton1 instance = new Singleton1();
    
        /**
         * 创建实例的方法必须用static修饰
         */
        public static Singleton1 getInstance() {
            return instance;
        }
    }
    

    优点:实现方式简单,可靠。
    缺点:无法实现延迟加载,由于instance成员变量是static的,在jvm加载类时单例对象就会被创建,而不管该类是否能被用到。

    2.2 懒汉式单例

    public class Singleton2 {
    
        private Singleton2() {
            System.out.println("LazySingleton instance is create!!");
        }
    
        /**
         * instance赋值null,确保系统加载时没有额外负载
         */
        private static Singleton2 instance = null;
    
        /**
         * 注意:该方法必须是同步的,假如去掉同步关键字,在多线程环境下,加入线程正在新建单例,
         * 完成赋值前,线程2进行instance是否为null判断,可能被认为是null,从而会导致多个实例被创建
         * @return
         */
        public static synchronized Singleton2 getInstance() {
            if(instance == null){
                instance = new Singleton2();
            }
            return instance;
        }
    }
    

    优点:实现了延迟加载。
    缺点:由于引入了synchronized 关键字,再多线程的环境下它的耗时要远远大于饿汉式单例。

    2.3 静态内部类实现

    public class Singleton3 {
        private Singleton3() {
            System.out.println("StaticSingleton instance is create!!");
        }
    
        private static class SingletonHolder{
            private static Singleton3 instance = new Singleton3();
        }
    
        public static Singleton3 getInstance() {
            return SingletonHolder.instance;
        }
    }
    

    优点:使用内部类来维护单例的实例,当该类被jvm加载时,内部类不会被实例化,当要获取单例实例时才会加载SingletonHolder,对instance进行初始化。同时,实例的建立实在类加载时完成,所以是多线程安全的。因此该方法即支持延迟加载又是线程安全的,算是很完美了。
    缺点:有种极端情况,如果通过反射调用私有构造器依然会产生多个实例,一般不进行考虑。

    2.4 能被串行化的单例

    public class Singleton4 implements Serializable {
        private Singleton4() {
            System.out.println("Singleton instance is create!!");
        }
    
        private static Singleton4 instance = new Singleton4();
    
        public static Singleton4 getInstance() {
            return instance;
        }
    
        /**
         * 如果去掉该方法,在反序列化后依然生成多个实例。
         * 事实上,实现该方法后readObject()已经失效,返回值已经被readResolve()替代。
         * @return
         */
        private Object readResolve() {
            System.out.println("method [readResolve()] is invoked!!");
            return instance;
        }
    }
    

    测试代码

    class Test {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            Singleton4 s1 = null;
            Singleton4 s = Singleton4.getInstance();
            //将实例串行化到文件
            FileOutputStream fos = new FileOutputStream("Singleton4.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s);
            oos.flush();
            oos.close();
    
            //从文件读出原有的单例类
            FileInputStream fis = new FileInputStream("Singleton4.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            //readObject()已经失效,已经被readResolve()。
            s1 = (Singleton4) ois.readObject();
    
            System.out.println(s==s1);
        }
    }
    

    注意
    序列化和反序列化会破坏单例,一般来说这种场景不多见。

    只有把命运掌握在自己手中,从今天起开始努力,即使暂时看不到希望,也要相信自己。因为比你牛几倍的人,依然在努力。
  • 相关阅读:
    OC
    提取AppDelegate.m中的"RDVTabBarController"第三方框架的方法
    spring_aop
    spring_xml配置&依赖注入
    关于idea运行web项目时出现的浏览器问题
    Java中main方法参数类型个人粗略理解
    函数式编程_lambda
    反射_注解
    pl/sql使用小技巧
    触发器&索引&视图
  • 原文地址:https://www.cnblogs.com/freesky168/p/14358231.html
Copyright © 2020-2023  润新知