设计模式之单例模式

84 阅读2分钟

-# 单例模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。 下面便是单例模式的三种实现方式

饿汉式

//饿汉式单例,但是会浪费资源空间
public class Hungry {
​
    //构造器私有化
    private Hungry(){
​
    }
​
    //内部创建类的对象(要求此对象也必须声明为静态的)
    private static final Hungry HUNGRY = new Hungry();
​
    //提供公共的静态的方法,返回类的对象
    public static Hungry getInstance() {
        return HUNGRY;
    }
}
​

懒汉式

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
​
//懒汉式单例
//道高一尺,魔高一丈,还是没能解决反射造成的安全问题,想解决得看枚举
public class LazyMan {
​
    //这个的目的是解决俩次都是调用反射创建对象不安全的问题
    private static boolean delTwoFanShe = false;
​
    private LazyMan(){
​
//        System.out.println(Thread.currentThread().getName()+"ok");
​
        synchronized (LazyMan.class) {
​
            if(delTwoFanShe == false) {
                delTwoFanShe = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
​
//            if(lazyMan!=null) {
//                //此方式是解决创建实例时一个用的getInstance(),另一个用的反射得到的,但是解决不了俩次都是通过反射获得的情况
//                throw new RuntimeException("不要试图使用反射破坏异常");
//            }
        }
​
    }
​
    private volatile static LazyMan lazyMan = null;
​
    //双重检测锁模式的懒汉式模式,简称DCL懒汉式
    public static LazyMan getInstance() {
        //上锁
        if(lazyMan==null) {
            synchronized (LazyMan.class) {
                if(lazyMan==null) {
                    lazyMan = new LazyMan();//不是一个原子性操作
                    /**
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     * 假如不是按照123步骤执行的话,就是发生了指令重排,那么lazyMan就没有完成初始化操作
                     */
                }
            }
        }
​
        return lazyMan; //如果不是按顺序执行123操作的话,此时lazyMan还没有完成构造,所以必须加上volatile来避免这种问题的发生
    }
​
​
    //我们写main函数来破坏,不断增强懒汉式的安全性
    public static void main(String[] args) throws Exception {
​
//        for(int i=0;i<10;i++) {
//            new Thread(()->{
//                LazyMan.getInstance();
//            }).start();
//        }//        LazyMan instance = LazyMan.getInstance();  //这是一般的获取方式
​
        //我们得到自己设置的隐藏值,进行破坏
        Field delTwoFanShe = LazyMan.class.getDeclaredField("delTwoFanShe");
        delTwoFanShe.setAccessible(true);
​
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
​
        declaredConstructor.setAccessible(true);
​
        LazyMan lazyMan = declaredConstructor.newInstance();//通过反射得到实例对象
​
        //我们修改第一次的实例,来将值变成false,进行破坏
        delTwoFanShe.set(lazyMan,false);
​
        LazyMan lazyMan1 = declaredConstructor.newInstance();//俩次实例对象都由反射来实现,造成破坏
​
        System.out.println(lazyMan);
        System.out.println(lazyMan1);
​
    }
​
}
​

静态内部类实现

//静态内部类实现
public class Holder {
​
    private Holder() {
​
    }
​
    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }
​
    public static class InnerClass{
​
        private static final Holder HOLDER = new Holder();
​
    }
​
}
​