单例模式

单例模式

核心作用:

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

常见场景:

  • Windows的任务管理器
  • Windows的回收站
  • 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
  • 网站的计数器一般也会采用单例模式,可以保证同步
  • 数据库连接池的设计一般也是单例模式
  • 在Servlet编程中,每个Servlet也是单例的
  • 在Spring中,每个Bean默认就是单例的
  • ……

优点:

  • 由于单例模式只生成一个实例,减少了系统性能开销
  • 单例模式可以在系统设置全局的访问点,优化共享资源访问
  • 单例模式可以说只要是一个合格的开发都会写,但是如果要讲究,小小的单例模式可以牵扯到很多东西

常见的五种单例模式实现方式:

  • 饿汉式(线程安全,调用效率高,不能延时加载)
  • 懒汉式(线程安全,调用效率不高,可以延时加载)
  • DCL懒汉式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
  • 饿汉式改进:静态内部类式(线程安全,调用效率高,可以延时加载)
  • 枚举单例(线程安全,调用效率高,不能延时加载)
// 饿汉式单例
public class SingletonDemo01 {
    // 1. 私有化构造器
    private SingletonDemo01() {

    }
    
    // 2. 类初始化的时候,立即加载该对象
    private static SingletonDemo01 instance = new SingletonDemo01();
    
    // 3. 提供获取该对象的方法,没有synchronized,效率高
    public static SingletonDemo01 getInstance() {
        return instance;
    }
}

class SingleDemo01Test{
    public static void main(String[] args) {
        SingletonDemo01 instance = SingletonDemo01.getInstance();
        SingletonDemo01 instance2 = SingletonDemo01.getInstance();

        System.out.println(instance == instance2); // true
    }
}
// 懒汉式单例
public class SingletonDemo02 {
    // 1. 私有化构造器
    private SingletonDemo02() {

    }

    // 2. 类初始化的时候,不立即加载该对象
    private static SingletonDemo02 instance;

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

class SingleDemo02Test{
    public static void main(String[] args) {
        SingletonDemo02 instance = SingletonDemo02.getInstance();
        SingletonDemo02 instance2 = SingletonDemo02.getInstance();

        System.out.println(instance == instance2); // true
    }
}
// DCL懒汉式
public class SingletonDemo03 {
    // 1. 私有化构造器
    private SingletonDemo03() {

    }

    // 2. 类初始化的时候,不立即加载该对象
    private volatile static SingletonDemo03 instance;

    // 3. 提供获取该对象的方法,有synchronized,效率较低
    public static SingletonDemo03 getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo03.class) {
                if (instance == null) {
                    instance = new SingletonDemo03();
                }
            }
        }
        return instance;
    }
    // 1.分配内存
    // 2.执行构造方法
    // 3.指向地址
}

class SingleDemo03Test{
    public static void main(String[] args) {
        SingletonDemo03 instance = SingletonDemo03.getInstance();
        SingletonDemo03 instance2 = SingletonDemo03.getInstance();

        System.out.println(instance == instance2); // true
    }
}
// 静态内部类实现
public class SingletonDemo04 {
    private SingletonDemo04(){}

    private static class InnerClass {
        private static final SingletonDemo04 instance = new SingletonDemo04();
    }

    public static SingletonDemo04 getInstance() {
        return InnerClass.instance;
    }
}

class SingleDemo04Test{
    public static void main(String[] args) throws Exception {
        SingletonDemo04 instance = SingletonDemo04.getInstance();
        SingletonDemo04 instance2 = SingletonDemo04.getInstance();

        System.out.println(instance == instance2); // true

        // 反射机制:可以破坏以上的单例
        Constructor<SingletonDemo04> declaredConstructor = SingletonDemo04.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        SingletonDemo04 instance3 = declaredConstructor.newInstance();
        System.out.println(instance == instance3); // false
        System.out.println(instance.hashCode()); // 1735600054
        System.out.println(instance3.hashCode()); // 21685669
    }
}

在构造时判断,防止反射的破坏

// DCL懒汉式
public class SingletonDemo03 {
    // 1. 私有化构造器
    private SingletonDemo03() {
        synchronized (SingletonDemo03.class) {
            if (instance != null) {
                throw new RuntimeException("不要戏图用缓射来破fuai我们的单例");
            }
        }
    }

    // 2. 类初始化的时候,不立即加载该对象
    private volatile static SingletonDemo03 instance;

    // 3. 提供获取该对象的方法,有synchronized,效率较低
    public static SingletonDemo03 getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo03.class) {
                if (instance == null) {
                    instance = new SingletonDemo03();
                }
            }
        }
        return instance;
    }
    // 1.分配内存
    // 2.执行构造方法
    // 3.指向地址
}

class SingleDemo03Test{
    public static void main(String[] args) throws Exception {
        SingletonDemo03 instance = SingletonDemo03.getInstance();
        SingletonDemo03 instance2 = SingletonDemo03.getInstance();

        System.out.println(instance == instance2); // true

        Constructor<SingletonDemo03> declaredConstructor = SingletonDemo03.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        SingletonDemo03 instance3 = declaredConstructor.newInstance();
        System.out.println(instance == instance3); // false
        System.out.println(instance.hashCode()); // 1735600054
        System.out.println(instance3.hashCode()); // 21685669
    }
}

直接利用反射创建对象,单例模式依旧被破坏

class SingleDemo03Test{
    public static void main(String[] args) throws Exception {
        Constructor<SingletonDemo03> declaredConstructor = SingletonDemo03.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        SingletonDemo03 instance3 = declaredConstructor.newInstance();
        SingletonDemo03 instance4 = declaredConstructor.newInstance();
        
        System.out.println(instance3 == instance4); // false
        System.out.println(instance3.hashCode()); // 1735600054
        System.out.println(instance4.hashCode()); // 21685669
    }
}

在私有化构造器中添加标志位进行判断,可以解决问题

// DCL懒汉式
public class SingletonDemo03 {
    private static boolean flag = false;
    // 1. 私有化构造器
    private SingletonDemo03() {
        synchronized (SingletonDemo03.class) {
            if (flag == false) {
                flag = true;
            } else {
                throw new RuntimeException("不要戏图用缓射来破fuai我们的单例");
            }
         }
    }

    // 2. 类初始化的时候,不立即加载该对象
    private volatile static SingletonDemo03 instance;

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

class SingleDemo03Test{
    public static void main(String[] args) throws Exception {
        Constructor<SingletonDemo03> declaredConstructor = SingletonDemo03.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        SingletonDemo03 instance3 = declaredConstructor.newInstance();
        SingletonDemo03 instance4 = declaredConstructor.newInstance();

        System.out.println(instance3 == instance4); 
        System.out.println(instance3.hashCode()); 
        System.out.println(instance4.hashCode()); 
    }
}

但是仍然存在问题,反射可以通过将flag置为false再进行创建。

由于通过反射获得构造函数,在实例化调用newInstance()方法时,会进行判断,当类型是枚举时,将抛出异常,“不能反射地创建一个枚举对象”。

	@CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

因此,枚举单例是安全的,可以天然地屏蔽单例被反射带来的破坏。

// 枚举单例
public enum  SingletonDemo05 {
    INSTANCE;

    public SingletonDemo05 getInstance() {
        return INSTANCE;
    }
}

class  SingletonDemo05Test {
    public static void main(String[] args) {
        SingletonDemo05 instance = SingletonDemo05.INSTANCE;
        SingletonDemo05 instance2 = SingletonDemo05.INSTANCE;

        System.out.println(instance == instance2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值