设计模式--单例模式

一、概述

今天学习SpringMVC的时候,注意到Controller是一个单例模式,虽然是最简单的设计模式,但是发现自己对其并没有太多了解,于是准备学习下。

二、单例模式的五种形式分析

注意下,这里提到的5种形式并不是说单例模式只有这5种模式,而是我通过查找资料总结出来的。

1、饿汉式(不推荐)

代码如下:


    /**
     *  饿汉式
     */
    class SingletonDemo1 implements Serializable {

        private static SingletonDemo1 instance = new SingletonDemo1() ;

        private SingletonDemo1(){}

        public static SingletonDemo1 getInstance(){
            return instance ;
        }

    }

这种模式的特点是在申明静态类对象的时候实例就已经创建出来了。

  • 优点:可以每次很快的获取实例,速度较快。
  • 缺点:优点浪费内存资源,有时候我们需要在需要的时候创建。

2、懒汉式(不推荐)


    /**
     *  懒汉式
     */
    class SingletonDemo2{

        private static  SingletonDemo2 instance  ;

        private SingletonDemo2(){} 

        public static synchronized SingletonDemo2 getInstance(){
            if( instance == null ){
                instance = new SingletonDemo2() ;
            }
            return instance ;
        }
    }

这种模式的特点是在需要实例的时候创建。主要在getInstance()方法加了锁,主要防止多线程并发问题。

  • 优点:由于它是在需要的时候才创建,和第一种模式的最大区别就是节省了资源。
  • 缺点:它的缺点也是比较明显的,第一次获取实例时会比较慢,因为需要初始化,最大的问题是每次获取都需要同步,增加了额外的开销。

3、两次检查(推荐)

这个模式的具体叫什么名字我也不知道。。。开代码吧


    class SingletonDemo3{

    private static volatile SingletonDemo3 instance ;

    private SingletonDemo3(){}

    public static SingletonDemo3 getInstance(){
        if( instance == null ){
            synchronized (SingletonDemo3.class) {
                if( instance == null ){
                    instance = new SingletonDemo3() ;
                }
            }
        }

        return instance ;
    }
}

从代码看,和第二种有点类似,但是好像又多了点什么。它对instance做了两次判断,第一次判断是解决不必要的同步,第二次则是在instance为null的条件下才创建。

  • 优点:解决了第二种最大的同步问题,资源利用率高。
  • 缺点:在第一次获取instance较慢。

4、静态内部类模式(推荐)

这种模式我以前也没见到过,查资料的。


    class SingletonDemo4{

    private static SingletonDemo4 instance ;

    private SingletonDemo4(){}

    public static SingletonDemo4 getInstance(){
        return SingletonHolder.instance ;
    }

    static class SingletonHolder{
        public static SingletonDemo4 instance = new SingletonDemo4() ;
    }
}

  • 优点:第一次加载类时不会读instance实例化,只有在调用getInstance()才会实例化,而且它是线程安全的。(至于为什么是线程安全的,我们知道导致线程不安全的前提肯定不会只有一行代码)
  • 缺点:这种模式好像并没有太明显的缺点。

5、容器模式


    class SingletonDemo5{

    private static Map map = new HashMap();

    private SingletonDemo5(){}

    public static void registerService(String key,Object instance){
        if(!map.containsKey(key)){
            map.put(key, instance);
        }
    }

    public static Object getInstance(String key){
        return map.get(key);
    }
}

这种模式的特点是通过一个Map管理多种单例,

  • 优点:可以帮助实现解耦

三、细节

单例模式是不是在任何情况下都不会重新创建呢?答案是否定的,因为我们在序列化将Instance写入本地文件,然后再将其读出,两者是不一样的。


    //将SingletonDemo1对象写入序列化
    SingletonDemo1 instance = SingletonDemo1.getInstance();
    System.out.println("序列化之前:" + instance );
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("a.txt")));
    oos.writeObject(instance);
    oos.close() ;

    //将SingletonDemo2对象反序列化
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("a.txt"))) ;
    SingletonDemo1 instance2  = (SingletonDemo1) ois.readObject();
    System.out.println("反序列化之后:" + instance2 );

程序输出结果


    序列化之前:com.lw.singleton.SingletonDemo1@659e0bfd
    反序列化之后:com.lw.singleton.SingletonDemo1@5c647e05

从结果可以看出,在反序列化的过程中调用了SingletonDemo1对象的私有构造函数

四、总结

不管以哪种单例模式,其核心思想是一样的,首先将其构造函数私有化,接着通过静态方法获取实例,然后就是性能的问题了,具体选择哪一种看具体的情况。

OK,这篇就简单学习了下单例模式

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值