设计模式之单例模式

目录

一  单例模式的应用场景

二  饿汉式单例

三  懒汉式单例

四  注册式单例

五  破坏单例的方式


一  单例模式的应用场景

单例模式(Singleton Pattern)是指一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

例:公司的CEO

二  饿汉式单例

饿汉式单例模式是在类加载的时候就立即初始化了,并创立对象,不存在线程安全问题。

优点:没有加锁效率高,用户体验比懒汉好。

缺点:类加载的时候就初始化,无论是否使用都占用着空间,浪费内存。

代码如下:

public class HungrySingleton {
    private static final HungrySingleton hungrySingleton = new HungrySingleton();
    //构造私有化
    private HungrySingleton() {}

    public static HungrySingleton getInstence() {
        return hungrySingleton;
    }
}
下面是静态代码块饿汉模式/

public class HungryStaticSingleton {
    private static final HungryStaticSingleton hungrySingleton ;
    static {
        hungrySingleton = new HungryStaticSingleton();
    }
    //构造私有化
    private HungryStaticSingleton() {}

    public static HungryStaticSingleton getInstence() {
        return hungrySingleton;
    }
}

三  懒汉式单例

在外部掉用的时候才会加载

优点:在掉用的时候才会初始化节约内存

缺点:存在线程不安全问题,。

代码如下:

public class LazySingleton {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() { }
    //调用的时候才开始加载
    //加入synchronized关键字保证线程安全
    public synchronized static  LazySingleton getInstence() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}
//兼顾效率,并且达到线程安全目的双重检查锁

public class LazyDoubleCheckSingleton {
    private static LazyDoubleCheckSingleton lazySingleton = null;

    private LazyDoubleCheckSingleton() {}

    //调用的时候才开始加载
    //双重检索锁  -----进行两次判断
    public static LazyDoubleCheckSingleton getInstence() {

        if (lazySingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazySingleton;
    }
}

 

虽然改良版的要好很多但是有synchronized自然就会影响效率   当然还有一种终极版本静态内部类方式代码如下:

public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton() {}
    //1.单例空间共享
    //2.保证不会被重写/覆盖
    public static final LazyInnerClassSingleton getInstance() {
        return LazyClass.LAZY;
    }
    //默认不加载
    private static class LazyClass {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
}

四  注册式单例

注册式单例有两种一为枚举型单例,容器式单例

枚举创建的单例无法被反射破坏,无法被反序列化破坏,并且线程安全。

代码如下:

public enum EnumSingleton {
    INSTENCT;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstenct() {
        return INSTENCT;
    }
}

 

容器缓存方式的优点在于创建实例非常多的情况,便于管理,但是这是线程不安全的。

代码如下:

public class ContainerSingletion {
    private ContainerSingletion() { }

    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getBean(String clssName) {
        synchronized (ioc){
            if (!ioc.containsKey(clssName)) {
                Object obj = null;
                try {
                    obj = Class.forName(clssName).newInstance();
                    ioc.put(clssName, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(clssName);
            }
        }

    }
}

五  破坏单例的方式

破坏单例模式有两种: 反射强行破坏,序列化与反序列化的破坏。

       Class<?> clzz = LazyInnerClassSingleton.class;
            //拿到LazyInnerClassSingleton的构造函数
            Constructor constructor = clzz.getDeclaredConstructor(null);
            constructor.setAccessible(true);
            //调用两次构造方法
            Object o1 = constructor.newInstance();
            Object o2 = constructor.newInstance();
            System.out.println(o1 == o2);
----------------------------------------------------------------------------------
   // 上面则是反射破坏: 
   // 解决办法:就拿静态内部类举例  

  //在构造方法中加入判断防止反射强行入侵
    private LazyInnerClassSingleton() {
        if (LazyClass.LAZY==null){
            throw  new RuntimeException("还想进来?此路不通!!!");
        }
    }
       

序列化与反序列化破坏: 我们将一个单例创建好写入磁盘,下次再使用的时候从磁盘中读取出来,反序列化转成内存对象会重新分配内存,即重新创建对象。

解决办法:在单例类中重写readResolve()方法 返回当前对象。

   //重写这个方法防止序列化破坏
    private Object readResolve() {
        return INSTENCE;
    }

单例模式所有代码(含测试):https://github.com/sqwf/patten.git

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值