Singleton 最佳实践-单元素的枚举类型

接下来将分别介绍下实现 Singleton 的几种方法:

第一种:双重校验锁【不推荐使用】

public class Singleton
{
    private volatile static Singleton instance;

    private Singleton(){};

    public static Singleton getInstance()
    {
        if (instance == null)
        {
            synchronized (Singleton.class)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

饿汉模式:不会产生安全问题,因为在类加载的时候该对象就已经被创建好了

class DanLi {
     private DanLi(){} // 将构造方法私有化 让外部不能创建对象
     private static  DanLi dl = new DanLi(); // 将对象提前创建好,当类加载的时候就创建了对象
}

增强版:

class DanLi {
     private DanLi(){} // 将构造方法私有化 让外部不能创建对象
     private static  DanLi dl = new DanLi(); // 将对象提前创建好,当类加载的时候就创建了对象
     public static DanLi getDanLi(){ // 当调用方法时直接返回对象
           return dl;
     }
}

变种:在类初始化的时候实例化 INSTANCE

public class Singleton{
    private static Singleton instance = null;
    private Singleton(){};
    static {
        instance = new Singleton();
    }

    public static Singleton getInstance() {
        return instance;
    }
}

静态内部类

public class Singleton{
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    private Singleton() {};

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

不过,以上几种方法都存在一个共性的问题:它们都可以借助 AccessibleObject.setAccessible 方法通过反射机制,调用私有构造器创建新的实例

	一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,此时,就可以调用 AccessibleObject.setAccessible(boolean flag) 方法来允许这种访问

Singleton 最佳实践-单元素的枚举类型

if (s1 == s2) {
	// 说明创建了一个实例
} else {
	// 说明创建了两个不同的实例,如果需要抵御这种攻击,可以在被要求创建第二个实例的时候抛出异常
}

可修改如下:

public class ElvisModified  
{  
    private static boolean flag = false; // 增加标识 
 
    private ElvisModified(){  
        synchronized(ElvisModified.class) {  
            if(flag == false)  // 判断标识
            {  
                flag = !flag;  
            }  
            else 
            {  
                throw new RuntimeException("单例模式被侵犯!");  
            }  
        }  
    }  
 
    private  static class SingletonHolder{  
        private static final ElvisModified INSTANCE = new ElvisModified();  
    }  
 
    public static ElvisModified getInstance()  {  
        return SingletonHolder.INSTANCE;  
    }  
 
    public void doSomethingElse()  {  
 
    }  
}

JDK 1.5 之后,可使用单元素的枚举类型来实现 Singleton

public enum Test {
    INSTANCE;

    public void dosomething() {
        System.out.println(this + " is speaking!");
    }

}

// test
public class TestSingleton {

    public static void main(String[] args) {
        Test t1 = Test.INSTANCE;
        t1.dosomething();

        Test t2 = Test.INSTANCE;
        t2.dosomething();

        System.out.println(t1 == t2);
    }

}

// result
INSTANCE is speaking!
INSTANCE is speaking!
true

Singleton 最佳实践-单元素的枚举类型
使用单元素的枚举类型实现 Singleton 时,通过反射机制去创建新的实例时会抛出异常。和以前的 s1 == s2 对比,可以看到使用单元素的枚举类型在功能上与公有域方法相近,但是它更加简洁,并且无偿得提供了序列化机制,绝对防止多次实例化。