23种设计模式—①单例模式

单例模式:

保证一个类只有一个实例,并且提供一个该实例的全局访问点

单例模式的要素
  • 1.私有的静态的实例对象

  • 2.私有的构造函数(保证在该类外部,无法通过new的方式来创建对象实例)

  • 3.公有的、静态的、访问该实例对象的方法
    23种设计模式—①单例模式_第1张图片

1、饥汉式

public class Singleton1 {
    //1、私有化的构造方法
    private Singleton1(){

    }

    //2、类初始化的时候,立即加载该对象
    private static Singleton1 singleton1 = new Singleton1();

    //3、提供获取该对象的公共方法
    public staticSingleton1 getInstance(){
        return singleton1;
    }

}

优点: 线程安全、调用效率高

缺点: 在多实例或者有其他静态方法时,在启动时没有使用它的时候就已经加载好了,浪费内存。

2、懒汉式

public class Singleton2 {
    //1、私有的构造方法
    private Singleton2(){

    }

    //2、类初始化的时候,不立即加载该对象
    private static Singleton2 singleton2;

    //3、提供获取该对象的方法,有synchronized,效率较低
    public static synchronized Singleton2 getInstance(){
        return singleton2 = new Singleton2();
    }
}

优点: 线程安全,在没有使用时不用加载,节省内存。

缺点: 调用效率不高

3、DCL懒汉式

double check lock 双重检查加锁

public class Singleton3 {
    //1、私有化构造方法
    private Singleton3(){

    }

    //2、类初始化的时候,不立即加载该对象
    private static volatile Singleton3 singleton3;

    //3、提供获取该对象的方法,有synchronized,效率较低
    public synchronized Singleton3 getInstance(){
        if(singleton3 == null){
            synchronized(Singleton3.class){
                if(singleton3 == null) {
                    singleton3 = new Singleton3();
                }
            }
        }
        return singleton3;
    }
}

优点: 在没有使用时不用加载,节省内存,而且改善了 饿汉式效率低的问题

缺点: 在一种极端情况下,当第一个线程进入第二次if判断,开始创建对象,这时第二个线程进来开始进行第一个if判断,这样判断出对象已经非空了,于是直接return返回对象,但是第一个线程还没有执行结束,可能会发生意想不到的情况。所以可以给对象加volatile优化一下(使用volatile关键字修饰的变量会强制将修改的值立即写入主存),但不能完全避免问题。

4、通过静态内部类改进饥汉式

public class Singleton4 {
    private Singleton4(){

    }

    private static class InnerClass{
        private static Singleton4 singleton4 = new Singleton4();
    }

    public static Singleton4 getInstance(){
        return InnerClass.singleton4;
    }
}

优点: 线程安全、调用效率高,优化了饥汉式浪费内存的问题
缺点: 反射可以通过关闭权限检测,然后创建出的对象会破坏单例

class SingletonTest{
    public static void main(String[] args) throws Exception {
        Singleton4 instance1 = Singleton4.getInstance();
        Constructor<Singleton4> declaredConstructor = Singleton4.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Singleton4 instance2 = declaredConstructor.newInstance();

        System.out.println(instance1==instance2);
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

23种设计模式—①单例模式_第2张图片
可以看出,单例被破坏了
但是可以通过给构造方法里加线程锁判断来防止通过反射破坏单例,如果发现有人有破坏单例的行为,给他抛出一个运行时异常

    private Singleton4(){
        synchronized (Singleton4.class){
            if(InnerClass.singleton4!=null){
                throw new RuntimeException("不要试图用反射破坏我们的单例");
            }
        }
    }

23种设计模式—①单例模式_第3张图片

5、使用枚举

public enum  Singleton5 {
    SINGLETON_5;

    public Singleton5 getSingleton5(){
        return SINGLETON_5;
    }
}

优点: 枚举底层代码不允许使用反射创建对象,所以用枚举创建单例是安全的

缺点: 不能延时加载

你可能感兴趣的:(23种设计模式)