Singleton design pattern
This is one of the creational patterns described by
GoF. This pattern ensures that a class must have one and only one instance provided a global access point to it and also with lazy initialization (initialization on first use).
So, to meet the above requirements, few steps of implementation are coming into our mind :
- define private no-argument constructor
- declare private static variable of the class type as instance and initialize
- define a public static method as access point of the instance and return the instance
Approach - 1 (Eager Initialization)
This is the simplest implementation of a singleton class. But the concerns here are :
1. Eager initialization
2. Missing exception handling if there is chances of getting any exception during object construction
Approach - 2 (Eager Initialization with Exception handling)
Here the exception handling is included but still is an eager initializsation. To achieve lazy initialization, we have to instantiate the object on demand (on first access).
Approach - 3(Lazy Initialization with Exception handling)
Here both above concerns are addressed (lazy initialization and exception handling) and the code looks fine. But it doesn't mean this is the efficient implementation. This implementation has so many concerns :
1. Not thread safe
2. Can be broken by serialisation if the class is serializable
For thread safe, that we can achieve through synchronisation (either by defining synchronized method or synchronized block with double check lock while instantiating the object).
Approach - 4
And in the previous approach (Approach - 1 and Approach - 2), during the eager initialization, the implementation is completely safe in multi threaded environment as the object is instantiated during the class loading. But we can't achieve the lazy initialization in this approach. To overcome this Bill Pugh came up with a new approach to create singleton class using a static inner class.
Approach - 5 (Bill Pugh's approach using static inner class to achieve thread safety and lazy loading)
Here SingletonHelper class is a static inner class. And this will not be loaded with the Singleton class. When ever the Singleton.getInstance() method will be invoked, and the SingletonHelpet.instance will be access for the first time, the helper class will be loaded at the same time. Using this approach the lazy initializaton is achieved easily and also thread safe.
Now, let's discuss about the other concern - "
Can be broken by serialisation if the class is serializable". Here we can apply Java's magic method trick to resolve these kind of problems. Java provided some hidden and tricky method (
readResolver() ) for developer to replace the deserialized object with the required object.
Approach - 6
The readResolve() is called when ObjectInputStream reads the object from stream and trying to return to the caller. Here we have returned the SingletonHelper.instance, so the deserialized object will be replaced by this instance.
After all these approach, you might have decides already to go with the Approach - 6 as it overcomes all the concerns :
1. No more eager initialization
2. Thread safe in multi threaded environment
3. Safe with Serialization
After all looks fine, now we will discuss about the real devil for all these approaches that is Java Reflection. This is the mechanism by which any kind of class (even with private constructor, private members) are also can be accessed by creating new instance of that class. So the above approach can be broken with this mechanism.
To overcome this, we have to go for the best approach for implementing singleton class proposed by
Joshua Bloch in
Effective Java "a single-element enum type is the best way to implement a singleton".
Approach - 7 (The best for Serialization/Reflection attacks but eager initialization)
Here in this above example I have demonstrated the same previous required singleton class using enum type.
The below are the reasons why this approach is the best to implement a singleton class.
1. Java gives guarantee that enum can not be broken by Reflection API : We can not create
instance of an enum using Reflection API
2. Serialization process is different for enum than a normal class : During serialization,
ObjectOutputStream writes the value returned by enum constant's name() method. And to
deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the
deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing
the constant's enum type along with the received constant name as arguments.
Above all benefits, still enum type approach has the concern of eager initialization. We can not achieve Lazy Initialization in this approach but more robust for serialization/reflection attack. If you have more concerns about eager initialization, then you can go with Approach - 6 but again the devil (reflection API) can pull it down.
Please feel free to give your comment/suggestion. Thank you.