学习笔记:单例模式

单例模式:单例模式是什么有啥用什么时候用为什么要用?怎样用?先来一波素质五连

是什么?

单例模式?字面上理解,单:一个,例:实例,也就是我们所说的对象

有啥用?

是为了资源的重复利用,只需要赋值或者初始化一次,大家就都能重复使用

什么时候用?为什么要用?(只有一份的如日历,只需要一份的如IOC容器)

Listener 监听器,Calender 日历类,IOC容器类,配置信息Config(一搬是单例)这些都是单例的

怎样用?(这个一句话说不了,容我细细道来)

下面我将用饿汉式,懒汉式,最牛逼单例模式,枚举,注册登记式反序列化如何保证单例 单例模式的运用形式进行详细说明

 

  1. 饿汉式(有处明显错误导致无法实现单例,答案暗藏在后文中)

什么叫饿汉式?我的理解就是,饿了就迫不及待想吃,看下面第9行代码,声明为

private static final:说明类加载时就初始化就初始化对象,然后静态的,所以对象一直保存,直到程序停止

缺点:自然就是无论你用不用这个对象,在加载这个类的时候他都给你初始化了,都会占用空间,直到程序停止

优点:很明显,因为它在类被加载时就初始化,所以不存在对象创建时的并发问题,也就是不存在线程安全问题,可以保证单例,而且效率也算不错,因为当创建对象时需要的那几纳秒省了

建议应用场景:如果某个类的对象只需要一份,并且使用非常频繁,可以使用这种模式

 

  1. 懒汉式(明说了,答案就在下图)

什么是懒汉式?我的理解就是,懒人特点了解下,什么事情都不急着提前准备,需要用时才去准备

我们可以看到,懒汉模式下的对象,我们可以在需要时再调用方法得到,并且能保证单例(能保证?)下面就来在优缺点里继续分析

优点:避免了内存的浪费,需要对象时才获得,而且非高并发时基本能保证单例

缺点:很明显,上面都说了非高并发时能保证单例,那么高并发时不能保证一定单例, 怎样解决呢,在方法前面加个synchronized关键字(如果对性能要求不高的话可加,毕竟同步锁要等待,不懂的可以去了解下同步锁synchronized)

  1. 既然上面或多或少都有缺点,下面再来个懒汉式改进版(也就是标题的最牛逼版,请不要叫我标题档...)

优点:兼顾了饿汉模式的内存占用问题与synchronized的性能问题(性能我有测试,创建200W个对象速度跟饿汉模式基本没区别),测试代码就不贴了,自己动手试试

缺点:欢迎留言

注册式单利

注册式单例模式又称为等级式单例模式,就是将每一个实例都登记到某一个地方,使用唯一的标识获取实例。注册式单例i模式有两种:一种是枚举式单例模式,另一种是容器式单例模式。

1.枚举式单例模式

  枚举式单例模式写法如下:

复制代码

public enum EnumSingleton {
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

复制代码

  测试代码如下:

复制代码

    @Test
    void EnumSingletonTest(){
        EnumSingleton e1 = null;
        EnumSingleton e2 = EnumSingleton.getInstance();
        e2.setData(new Object());
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("EnumSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(e2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("EnumSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            e1 = (EnumSingleton)ois.readObject();
            ois.close();
            System.out.println(e1.getData());
            System.out.println(e2.getData());
            System.out.println(e1.getData() == e2.getData());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

复制代码

  运行结果如下:

  枚举类型其实通过类名和类对象找到一个唯一的枚举对象。因此,枚举对象不可能别类加载器加载多次。

  那么反射能否破坏枚举式单例模式呢?测试代码如下:

复制代码

    @Test
     void EnumSingletonTestThread() {
        try{
            Class clazz = EnumSingleton.class;
            Constructor c = clazz.getDeclaredConstructor();
            c.newInstance();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

复制代码

  运行结果如下:

 报错没有找到无参构造方法。查看 Enum 源码  他的构造方法只有一个 protected 类型的构造方法,代码如下:

  protected Enum(String name, int ordinal){ this.name = name; this.ordinal = ordinal; }  

  再做如下测试:

复制代码

    @Test
     void EnumSingletonTestThread() {
        try{
            Class clazz = EnumSingleton.class;
            Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
            c.setAccessible(true);
            EnumSingleton enumSingleton = (EnumSingleton)c.newInstance("l-coil",666);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

复制代码

   测试结果如下:

  

  报错已经很明显了,不能用反射来创建枚举类型。

  枚举式单例模式也是 Effective Java 书中推荐的一种单例模式实现写法。JDK 枚举的语法特殊性质及繁殖也为枚举报价护航,让枚举式单例模式成为一种比较优雅的实现。 

2.容器式单例

  容器式单例写法如下:

复制代码

public class ContainerSingleton {
    private ContainerSingleton(){}
    private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
    public static Object getBean(String className){
        synchronized (ioc){
            if(!ioc.containsKey(className)){
                Object obj = null;
                try{
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                }catch (Exception e){
                    e.printStackTrace();
                }
                return obj;
            } else {
              return ioc.get(className);
            }
        }
    }
}

复制代码

  容器式单例模式使用与实例非常多的情况,编辑管理。单它是非线程安全的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值