zl程序教程

您现在的位置是:首页 >  其他

当前栏目

单例设计模式的正确写法(双重校验,静态内部类,枚举)

2023-04-18 12:32:29 时间

单例设计模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统系统中一个类只有一个实例。属于创建型模式

特点:

  1. 单例模式类只有一个实例(对象)
  2. 单例模式类必须自己创建自己的唯一实例
  3. 单例模式类必须给所有其他对象提供这一实例

2.1 懒汉式

普通的懒汉式由于懒加载所以存在线程安全问题,这里给出有一种双重检查的实现方式

public class Singleton{
    private Singleton(){}
    private volatile static Singleton s = null;
    
    
    public static Singleton getInstance(){
        if(s == null){
            // 两个线程都进入到此处,必须在下面在进行空判断,否则创建两次
            synchronized(Singleton.class){
                // 防止两个线成同时进入上面的空判断
                if(s == null){
                    // new 关键字可能因为出现指令重排出现问题,所以外部加上volatile
                    s = new Singleton(); 
                }
            }
        }
        return s;
    } 
}

2.2 饿汉式

public class Singleton{
    private Singleton(){}
    public static final Singleton s = new Singleton();
    public static Singleton getInstance(){
        return s;
    }  
}

2.3 静态内部类

public class Singleton{
    private static class LasyHolder{
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton(){}
    public static Singleton getInstance(){
        reuturn LazyHolder.INSTANCE;
    }
}

注意事项:

  1. 从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到该单例对象
  2. INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,只有当调用getInstance方法,使得静态内部类LazyHolder被加载的时候。因为这种方式是利用classLoader的加载机制来实现懒加载,并保证构建单例的线程安全。
  3. 无法防止反射来重复构建对象

利用反射打破单例:

Constructor con = Singleton.class.getDeclaredConstructor();
con.setAccessible(true);
Singleton s1 = (Singleton) con.newInstance();
Singleton s2 = (Singleton) con.newInstance();

sout(s1.equals(s2)); //false

除了反射攻击外,还可能存在使用反序列化攻击情况

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>
Singleton instance = Singleton.getInstance();
byte[] serialize = SerializationUtils.serialize(instance);
Singleton newInstance = SerializationUtils.deserialize(serialize);
System.out.println(instance == newInstance);//false, 代表两个不同对象,与单例违背

2.4 枚举

public enum Singleton{
    INSTANCE;
    
    public void dosth(){
        System.out.println("do sth");
    }
}

调用:

public static void main(String[] args){
	Singleton.INSTANCE.dosth();
}