Singleton Java enum

When you create a singleton class, consider using enumeration. It solves the problem might be due to the de-serialization and reflection occur.

In each embodiment should be single JVM only one instance of the class. Singleton class Same instance is reused multiple threads. In most cases, we use a single example to represent the system configuration and window manager, as these examples should be JVM shared by all the threads and objects. [Lesson] You Rui unraveling elaborate architecture of those things unraveling the elaborate architecture of those things

The method of making a single example of the conventional

There are several methods of making single embodiment popular.

Method 1 : public static final embodiment having a single field

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

 

Method 2 singleton public static factory method:

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

 

Method 3 : a single embodiment of lazy initialization

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

 

Advantages and disadvantages of the above methods

All of these methods use a private constructor enforcement does not meet the requirements (not create an instance). Here, even if we do not have anything to do, we can not avoid the creation of private constructor. Because if you do, it will be used to create the same kind of access modifier an implicit no-argument default constructor. For example, if the class is declared as public , then the default constructor for the public . Otherwise, the default constructor is public . If the class is declared as protected , the default constructor is protected (For more detailed information, please refer to the Oracle documentation).

Comparison of the above methods, the first two methods there is no difference in performance. Method 1 clearer, simpler. Method 2 a small advantage is that, in the future, you can become the kind of non-single class, without changing the API . You can change the factory method implementation creates a new instance for each call, instead of returning the same instance as a way to achieve the following.

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

 

Static fields are initialized when the class is loaded. Thus, in the methods 1 and 2 , even in the case where we do not use them at run time, will create a singleton instance. As long as singleton object is not too large and not too expensive to create an instance, there is no problem. Method 3 avoids the problem of lazy initialization. In the method 3 , the instance is created when we first visit singleton object. Be sure to use fine-grained synchronization objects for multiple concurrent threads created no more than one.

Before using the singleton class without serialization and deserialization, all of the above methods can work properly. Let's think about: How do we achieve a singleton behavior by the above method? By the constructor private, and constructors can not be used to create a new instance of the class to achieve. But in addition to the constructors, there is no other way to create an instance of the class do? The answer is no. There are other advanced methods.

1.      serialization and deserialization

2.      Reflection

Serialization and de-serialization problem

To the above-described sequence of singleton, we must use the Serializable interface classes. However, this is not enough. Deserialization class, creates a new instance. Now, whether private constructor does not matter. Now, the JVM may have multiple instances of the same class of a single embodiment, it violates the singleton attribute.

public class SerializeDemo implements Serializable {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        singleton.setValue(1);
        // Serialize
        try {
            FileOutputStream fileOut = new FileOutputStream("out.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(singleton);
            out.close();
            fileOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        singleton.setValue(2);
        // Deserialize
        Singleton singleton2 = null;
        try {
            FileInputStream fileIn = new FileInputStream("out.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            singleton2 = (Singleton) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException i) {
            i.printStackTrace();
        } catch (ClassNotFoundException c) {
            System.out.println("singletons.SingletonEnum class not found");
            c.printStackTrace();
        }
        if (singleton == singleton2) {
            System.out.println("Two objects are same");
        } else {
            System.out.println("Two objects are not same");
        }
        System.out.println(singleton.getValue());
        System.out.println(singleton2.getValue());
    }
}

 

上面代码的输出是:

Two objects are not same
2
1

 

在这里,singletonsingleton2是两个不同的实例,具有两个不同的值作为其字段变量。这违反了单例属性。解决方案是我们必须实现readResolve方法,该方法在准备反序列化的对象并将其返回给调用者之前准备好。解决方法如下。

public class Singleton implements Serializable{
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    protected Object readResolve() {
        return INSTANCE;
    }
}

 

现在,以上代码的输出将是:

Two objects are same
2
2

 

现在,保留了singleton属性,并且JVM中仅存在singleton类的一个实例。

反思问题

高级用户可以在运行时使用反射将构造函数的私有访问修饰符更改为所需的任何内容。如果发生这种情况,我们唯一的非不稳定机制就会中断。让我们看看如何做到这一点。

public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        Singleton singleton = Singleton.INSTANCE;
        Constructor constructor = singleton.getClass().getDeclaredConstructor(new Class[0]);
        constructor.setAccessible(true);
        Singleton singleton2 = (Singleton) constructor.newInstance();
        if (singleton == singleton2) {
            System.out.println("Two objects are same");
        } else {
            System.out.println("Two objects are not same");
        }
        singleton.setValue(1);
        singleton2.setValue(2);
        System.out.println(singleton.getValue());
        System.out.println(singleton2.getValue());
    }
}

 

输出:

Two objects are not same
1
2

 

这样,不可访问的私有构造函数变得可访问,并且使类成为单例的整个想法被打破。

Java中使用枚举创建单例

通过使用枚举类型使单例很容易解决所有上述问题。

枚举单例

public enum Singleton {
    INSTANCE;
}

 

上面的三行代码构成了一个单例,没有讨论任何问题。由于枚举本质上是可序列化的,因此我们不需要使用可序列化的接口来实现它。反射问题也不存在。因此,可以100%保证JVM中仅存在一个单例实例。因此,建议将此方法作为Java中制作单例的最佳方法

如何使用

public enum SingletonEnum {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
}

 

主类实现:

public class EnumDemo {
    public static void main(String[] args) {
        SingletonEnum singleton = SingletonEnum.INSTANCE;
        System.out.println(singleton.getValue());
        singleton.setValue(2);
        System.out.println(singleton.getValue());
    }
}

 

这里要记住的一件事是,当序列化一个枚举时,字段变量不会被序列化。例如,如果我们对SingletonEnum类进行序列化和反序列化,则将丢失int值字段的值(有关枚举序列化的更多详细信息,请参考Oracle文档)。

 


Guess you like

Origin blog.51cto.com/14631216/2458636