JAVA 单例模式

JAVA 单例模式
枚举单例模式:
关于单例模式的实现有很多种,网上也分析了如今实现单利模式最好用枚举,好处不外乎三点:
1.线程安全 2.不会因为序列化而产生新实例 3.防止反射攻击
1.线程安全 
下面这段代码就是声明枚举实例的通常做法,它可能还包含实例变量和实例方法,但是为了简单起见,我并没有使用这些东西,仅仅需要小心的是如果你正在使用实例方法,那么你需要确保线程安全(如果它影响到其他对象的状态的话)。默认枚举实例的创建是线程安全的,但是在枚举中的其他任何方法由程序员自己负责。
关于线程安全的保证,其实是通过类加载机制来保证的,我们看看INSTANCE的实例化时机,是在static块中,JVM加载类的过程显然是线程安全的。
static {};
  Code:
   0:   new     #12; //class com/abin/lee/spring/util/Singleton$1
   3:   dup
   4:   ldc     #14; //String INSTANCE
   6:   iconst_0
   7:   invokespecial   #15; //Method com/abin/lee/spring/util/Singleton$1."<init>":(Ljava/lang/String;I)V
   10:  putstatic       #19; //Field INSTANCE:Lcom/abin/lee/spring/util/Singleton;
   13:  iconst_1
   14:  anewarray       #1; //class com/abin/lee/spring/util/Singleton
   17:  dup
   18:  iconst_0
   19:  getstatic       #19; //Field INSTANCE:Lcom/abin/lee/spring/util/Singleton;
   22:  aastore
   23:  putstatic       #21; //Field ENUM$VALUES:[Lcom/abin/lee/spring/util/Singleton;
   26:  return
线程安全,从反编译后的类源码中可以看出也是通过类加载机制保证的,应该是这样吧

2.不会因为序列化而产生新实例
枚举自己处理序列化
传统单例存在的另外一个问题是一旦你实现了序列化接口,那么它们不再保持单例了,因为readObject()方法一直返回一个新的对象就像java的构造方法一样,你可以通过使用readResolve()方法来避免此事发生,看下面的例子:
//readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }
这样甚至还可以更复杂,如果你的单例类维持了其他对象的状态的话,因此你需要使他们成为transient的对象。但是枚举单例,JVM对序列化有保证。
优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象


3.防止反射攻击
反射攻击,我有自己试着反射攻击了以下,不过报错了...看了下方的反编译类源码,明白了,因为单例类的修饰是abstract的,所以没法实例化。(解决













静态内部类:
// Correct lazy initialization in Java 
@ThreadSafe
class Foo {
    private static class HelperHolder {
       public static Helper helper = new Helper();
    }
 
    public static Helper getHelper() {
        return HelperHolder.helper;
    }
}

它利用了内部静态类只有在被引用的时候才会被加载的规律。

这样一来,一旦内部的HelperHolder被引用了,它就会首先被JVM加载,进行该类的静态域的初始化,从而使得Helper这一单例类被初始化。它之所以是线程安全的,也是托了JVM的福,因为JVM对于类的加载这一过程是线程安全的。

你可能感兴趣的:(JAVA 单例模式)