Java设计模式之简单总结回顾

今天回头看之前的单例设计模式的有关记录,在此简单在总结下:

单例模式分为饿汉式和饱汉式(懒汉式),该两个方案在不考虑一些多线程、反射、序列化和反序列化的情况下,

饱汉式易于理解和书写,如下:

public class LazySingleton {
    /**
     * 初级懒汉式单例设计模式
     * 线程不安全的,单线程时不会出问题,但是多线程时会出问题
     * 假设两个线程同时运行,第一个线程在判断完if (lazySingleton == null)为true,
     * 进入到方法内,但是还未执行创建对象或者还未执行完创建对象的步骤时,
     * 此时lazySingleton 还是null,第二个线程刚好走到判断是否为null时,则判断为null,
     * 之后线程二也会进入该方法。这样两个线程都进入,则都会正常创建对象, 造成创建两个对象。
     *即使最后lazySingleton返回的对象一样(因为lazySingleton被赋值两次,后一次将前一次覆盖)
     * 也同样是存在安全隐患。
     * */
    private static LazySingleton lazySingleton = null;
    private LazySingleton(){

    }
    public static LazySingleton getInstance(){
        if (lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

饿汉式有两种写法:

(如下是添加了防反射攻击和序列化问题的写法)

饿汉一:

public class HungrySingleton implements Serializable{
    //写法简单的单例模式
    //类加载时就初始化,没有多线程问题,但是可能会造成资源浪费,
    //比如该对象没有被使用
    private final static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){
        //为构造器添加反射调用判断
        if (hungrySingleton != null){
            throw new RuntimeException("单例设计模式构造器禁止反射调用");
        }
    }
    public static HungrySingleton getHungrySingleton(){
        return hungrySingleton;
    }
    private Object readResolve(){
        return hungrySingleton;
    }
}

饿汉二:

public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton( ){
        //注意该方法不能少,否则会从外面创建一个对象出来。
        if (getStaticInnerClassInstance() != null){
            throw new RuntimeException("单例设计模式禁止反射调用私有构造器");
        }
    }
    private static class InnerClass{
        //该方案重点就在于如下私有类的初始化锁,重点看谁先获取到该初始化锁,
        //一旦获取到该初始化锁,会立刻执行初始化,并且该初始化过程对于其他
        //线程是不可见的。
        private static StaticInnerClassSingleton innerClassSingleton=
                new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getStaticInnerClassInstance(){
        return InnerClass.innerClassSingleton;
    }

}

对于如上防止反射攻击的方案,对于饱汉式或者说叫做懒加载的方案是不可行的。

一个简单,而且可以防止多种问题的方案应该属枚举类型做单例的方案了

public enum EnumInstance {
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static EnumInstance getEnumInstance(){
        return INSTANCE;
    }
}

和单例设计模式相关的一个常见应用为,单例和享元模式结合做成的容器单例:

public class ContainerSingleton {
    private ContainerSingleton(){

    }
    private static Map<String,Object> singletonMap = new HashMap<String, Object>();
    public static void putInstance(String key, Object instance) {
        if (key.length()>0 && instance != null) {
            if (!singletonMap.containsKey(key)) {
                singletonMap.put(key, instance);
            }
        }
    }
    public static Object getInstance(String key){
        return singletonMap.get(key);
    }

}

单例设计模式之防止克隆方法:

// 注意这里要实现克隆接口
public class HungrySingleton implements Serializable,Cloneable{
    //写法简单的单例模式
    //类加载时就初始化,没有多线程问题,但是可能会造成资源浪费,
    //比如该对象没有被使用
    private final static HungrySingleton hungrySingleton = new HungrySingleton();
    //也可以将上述改成如下方式

    private HungrySingleton(){
        if (hungrySingleton != null){
            throw new RuntimeException("单例设计模式构造器禁止反射调用");
        }
    }
    public static HungrySingleton getHungrySingleton(){
        return hungrySingleton;
    }
    private Object readResolve(){
        return hungrySingleton;
    }
    //注意这里的克隆方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值