Java Singleton Use Enumeration

Posted by Toxinhead on Sat, 14 Dec 2019 23:10:57 +0100

When creating a singleton class, consider using enumeration.It addresses issues that may arise due to deserialization and reflection.

A singleton is a class that should have only one instance in each JVM.Same instances of a singleton class are reused by multiple threads.In most cases, we use singletons to represent system configurations and window managers, since these instances should be common to all threads and objects in the JVM.Excellent Lessons Detailed architecture Those things Detailed architecture Those things

Traditional methods of making singletons

There are several popular ways to make singletons.

Method 1: An example with a common static final field

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

 

Method 2: An example of using the public static factory method

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

 

Method 3: An instance with delayed 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 the above methods use private constructors to enforce unsatisfactory (instances cannot be created).Here, even if we don't have anything to do, we can't avoid creating a private constructor.Because if you do, an implicit parameterless default constructor will be created using the same access modifiers as this class.For example, if the class is declared public, the default constructor is public; otherwise, the default constructor is public.If the class is declared protected, the default constructor is protected (see the Oracle documentation for more details).

Compared with the above methods, the two methods have no performance differences at all.Method 1 is clearer and simpler.One small advantage of method 2 is that you can make this class non-singleton in the future without changing the API.Instead of returning the same instance as below, you can create a new instance for each call by changing the implementation of the factory method.

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

 

Static fields are initialized when the class loads.Therefore, in methods 1 and 2, even if we do not use them at runtime, singleton instances will be created.As long as the single object is not too large and creating instances is not too expensive, there is no problem.Method 3 avoids the problem of delayed initialization.In Method 3, the instance was created the first time we accessed the singleton object.Fine-grained synchronization is used to ensure that no more than one object is created using multiple concurrent threads.

All of these methods work well until you serialize and deserialize without using a singleton class.Let's think again: How do we implement singleton behavior in the above way?This is done by making the constructor private and making it unavailable for creating new instances of classes.But is there no other way to create an instance of a class than a constructor?The answer is No.There are other advanced methods.

1.) Serialization and deserialization

2. Reflections

Serialization and deserialization issues

In order to serialize the above singleton classes, we must implement them using the Serializable interface.However, this is not enough.When the class is deserialized, a new instance is created.Now it doesn't matter whether the constructor is private or not.There may now be multiple instances of the same singleton class in the JVM, which violates the singleton property.

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());
    }
}

 

The output of the above code is:

Two objects are not same
2
1

 

Here, singleton and singleton2 are two different instances with two different values as their field variables.This violates the singleton property.The solution is what we have to do readResolve Method, which is prepared before the deserialized object is prepared and returned to the caller.The solutions are as follows.

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

 

Now the output of the above code will be:

Two objects are same
2
2

 

Now, the singleton property is preserved, and there is only one instance of the singleton class in the JVM.

Reflective Questions

Advanced users can use reflection at runtime to change the private access modifier of the constructor to anything they want.If this happens, our only non-instability mechanism will be interrupted.Let's see how we can do this.

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());
    }
}

 

Output:

Two objects are not same
1
2

 

In this way, inaccessible private constructors become accessible and the whole idea of making classes singletons is broken.

Create singletons in Java using enumerations

Using enumeration types makes it easy for a single case to solve all of these problems.

Enumerate singletons

public enum Singleton {
    INSTANCE;
}

 

The three lines of code above form an example and do not discuss any issues.Since enumerations are serializable in nature, we do not need serializable interfaces to implement them.Reflection problems also do not exist.Therefore, you can guarantee 100% that only one single instance exists in the JVM.Therefore, this method is recommended as the best way to make singletons in Java.

How to use

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

 

Main class implementation:

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());
    }
}

 

One thing to remember here is that when you serialize an enumeration, field variables are not serialized.For example, if we serialize and deserialize the SingletonEnum class, the value of the int value field will be lost (see the Oracle documentation for more details on enumeration serialization).

 


Topics: Java jvm Oracle