首页 > 其他 > 详细

单例模式详解(二)

时间:2021-01-12 23:24:47      阅读:29      评论:0      收藏:0      [点我收藏+]

  不知道哪位大神,提供了一种单例的写法,思考角度也很绝妙,从类初始化的角度考虑。这也是一种经典的单例实现方式,内部类单例实现。废话不多说,上代码:

  饿汉式单例(四)

  

/**
 * 内部类单例
 */
public class InnerClassSingleton {

    private InnerClassSingleton(){}


    public static InnerClassSingleton getInstance(){
        //在返回之前,一定要先加载内部类
        return Innerclass.instance;
    }

    //利用了java的语法特点,默认不加载内部类
    private static class Innerclass{
        private static final InnerClassSingleton instance = new InnerClassSingleton();
    }
}

  内部类的单例实现方式兼顾了饿汉式单例的内存浪费问题和synchronized加锁带来的性能损耗问题,并且内部类的实现方式只有在调用getInstance()方法的结果返回之前加载内部类,完成实例的初始化,巧妙的避免了线程安全问题,在不考虑序列化破坏单例和暴力反射破坏单例模式的情况下,这种实现方式堪称完美。接下来我们来看看暴力反射是如何破坏单例的:

  暴力反射破坏单例模式:

public static void main(String[] args) {
       try{
           Class<?> clazz = InnerClassSingleton.class;
           Constructor c = clazz.getDeclaredConstructor(null);
           c.setAccessible(true);
           //反射创建两个实例
           Object o1 = c.newInstance();
           Object o2 = c.newInstance();
         
           System.out.println(o1==o2); //false
       }catch (Exception e){
           e.printStackTrace();
       }

    }

  最终,在代码的最后一行一定是输出false,在内存中存在两个不同的实例,这样一来就违背了单例模式的初衷,有没有办法解决它呢?当然有,我们可以这优化内部类实现的单例模式:

/**
 * 内部类单例
 */
public class InnerClassSingleton {

    private InnerClassSingleton(){}
    
    public static InnerClassSingleton getInstance(){
        //加上判断,如果创建多个实例就抛出异常
        if(Innerclass.instance!=null){
            throw new RuntimeException("禁止创建多个实例!");
        }
        //在返回之前,一定要先加载内部类
        return Innerclass.instance;
    }

    //利用了java的语法特点,默认不加载内部类
    private static class Innerclass{
        private static final InnerClassSingleton instance = new InnerClassSingleton();
    }
}

  这样似乎就大功告成啦!

  当然除了以上几种常见的单例实现,还有一些单例实现方式:

  枚举式单例:

  

/**
 * 枚举单例
 */
public enum  EnumSingleton {
    INSTANCE;
    private Object object;

    public Object getObject(){
        return object;
    }

    public void setObject(Object object){
        this.object = object;
    }

    public static Object getInstance(){
        return INSTANCE;
    }
}

  枚举式单例是《Effective Java》书中比较推荐的一种单例实现方式,枚举的单例实现不仅是线程安全的,而且也能保证单例模式不会被破坏,可以说是一种既简单又安全的实现方式。

  容器式单例:

  看到这种实现的名字可能有些小伙伴会感到陌生,我们大名鼎鼎的spring框架的单例就是基于此方式进行实现的。容器式单例的实现方式适用于需要大量创建单例对象的场景,便于管理。我们直接来看看spring的源码实现:

  

private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //从缓存singletonObjects(实际上是一个map)中获取bean实例
        Object singletonObject = this.singletonObjects.get(beanName);
    //如果为null,对缓存singletonObjects加锁,然后再从缓存中获取bean,如果继续为null,就创建一个bean。
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            Map var4 = this.singletonObjects;
            //双重判断加锁
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
            //通过 singletonFactory.getObject() 返回具体beanName对应的ObjectFactory来创建bean。
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
        }  
    } 
}

  单例模式看似简单,其实里面还是还是有一些很深的学问值得研究的,比如我们的枚举式单例式怎么样防止恶意破坏的,这一点感兴趣的小伙伴可以自行研究一下,我这里就不贴出来了。单例模式的优点也有很多,比如:

  1、单例模式在内存中只有一个实例,可以减少内存开销;

  2、单例模式只提供一个全局的访问点,可以达到资源共享的目的。

  当然缺点也有,比如:

  1、单例模式没有接口,如果要扩展必须修改原来的代码,违背开闭原则;

  2、单例模式的功能代码通常写在一个类中,如果设计不合理,很容易违背单一职责原则。

  至此,单例模式的介绍就到此啦~

单例模式详解(二)

原文:https://www.cnblogs.com/chalice/p/14238366.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!