Singleton design pattern – restricting all 4 ways of Object creation in Java

In this article, we will discuss Singleton design pattern with example. Before delving more into topic, we will understand what is Singleton design pattern ?

Q) What is Singleton design pattern ?

Singleton design pattern is the

  • solution proposed to return same instance every time
  • restrict instantiation of a class more than once
  • exactly one copy is available at any given point of time
  • ensures only one instance is available in a Java Virtual Machine (JVM)

So, to create singleton design pattern or singleton class in Java, we need to restrict all possible ways to create object from outside of class

Now, we will revisit & understand what all possible ways to create object in Java

Different ways to create object in Java:

Primarily, there are only 4 ways to create object in Java, those are;

  • Using new operator or keyword
  • Using clone method of Object class
  • Using Object De-serialization
  • Using Reflection API & newInstance() method

Further using reflection API & newInstance() method, we can derive multiple ways to create objects in Java;

Using Reflection API & newInstance() method:

  1. Using Class.forName(“fully.qualified.name.of.class”).newInstance();
  2. Using <ClassName>.class.newInstance();
  3. Using <ClassName>.class.getClassLoader().loadClass(“fully.qualified.name.of.class”).newInstance();
  4. Using Constructor i.e.;
    Constructor<<className>> constructor = <ClassName>.class.getConstructor();
    <ClassName> object44 = constructor.newInstance();

Read here for complete details on various ways to create object in Java

 

Singleton class:

  • To create singleton class in Java, we need to suppress all possible ways to create object from outside of class
  • In short, we need to restrict all 4 (FOUR) possible ways to create object in Java
Way Object creation ways How to restrict 
1 new operator or keyword Provide private constructor, so that no-one outside of class can instantiate object;
2 clone() method of Object class Override clone() method by implementing Cloneable interface and throw CloneNotSupportedException();
3 Object de-serialization Override readResolve() method by implementing Serializable interface and return same INSTANCE every time;
4 Reflection API & newInstance() As we are already providing private constructor to restrict object creation using new operator from outside class;

This will help to restrict object creation using reflection API & newInstance() method;

because newInstance() method requires default public no-arg constructor for object creation from outside of class;

 

Steps to create singleton class in Java:

  1. Create INSTANCE of same class by instantiating class & this INSTANCE should be with private & static modifier
  2. Provide public static method that returns same INSTANCE of class every time
  3. Finally, create private constructor so that no-one create object from outside of class
  4. Providing private constructor helps to suppress creating objects either by new operator/keyword or refection API & newInstance() method
  5. If class implements Serializable interface, then override readResolve() method and return same INSTANCE
  6. If class implements Cloneable interface, then override clone() method and throw CloneNotSupportedException();

 

Eager or Lazy Instantiation:

Here, step 1 tells about creating object by instantiating class, it can be done 2 ways;

  1. Eager instantiation by directly creating object using new operator
  2. Lazy instantiatione.; creating object only after checking whether already singleton object is available or NOT

We will discuss 2 demo examples covering both approaches;

Assumption:

  • Class implements both Serializable & Cloneable interface
  • We are designing class in such a way that, when cloning is performed then respective overridden clone() method throws Clone not supported exception
  • Executing in a single-threaded environment

 

1. Eager Instantiation:

Here in Student class,

  • Directly one INSTANCE of the class is created/instantiated with private & static modifier
  • Next step is to provide public static method to access Singleton INSTANCE from outside class
  • Finally, providing private constructor for restricting object creation from outside of class
  • By implementing Serializable interface, we are returning same INSTANCE from readResolve(); method, as this is the one invoked during object de-serialization process
  • By implementing Cloneable interface, we are explicitly throwing clone not supported exception from overridden clone() method

Student.java

package in.bench.resources.singleton.design.pattern;
 
import java.io.ObjectStreamException;
import java.io.Serializable;
 
public class Student implements Serializable, Cloneable {
 
    // Eager-Instantiation: only-time INSTANCE created
    private volatile static Student INSTANCE = new Student();
 
    // private constructor
    private Student() {
        // helps to suppress creating objects either 
        // 1. by new operator/keyword or 
        // 2. by reflection API and newInstance() method
    }
 
    // create static method to get same instance every time
    public static Student getInstance(){
        return INSTANCE;
    }
 
    // to suppress creating new object during de-serialization
    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // directly throw Clone Not Supported Exception
        throw new CloneNotSupportedException();
    }
 
    // other utility methods and details of this class
}

 

2. Lazy Instantiation:

Here in Employee class,

  • We aren’t instantiating Employee INSTANCE directly, as against Student class in earlier eager-instantiation case
  • Rather INSTANCE is just declared
  • Later when object is requested invoking getInstance() method
  • Then method checks whether already instantiated singleton INSTANCE is available or NOT
  • This is instantiated only one time i.e.; for the very first time
  • Otherwise, from next request while invoking getInstance() method always returns same instantiated INSTANCE
  • This way performance improves
  • Assumption: above sentences applicable only for single-threaded environment
  • Note: There is serious problem in the below coded Employee class; while working with multi-threaded environment as there is a chance of 2 INSTANCEs getting created
  • Head-over to next section for more detail with examples and explanation while working in a multi-threaded environment

Employee.java

package in.bench.resources.singleton.design.pattern;
 
import java.io.ObjectStreamException;
import java.io.Serializable;
 
public class Employee implements Serializable, Cloneable {
 
    // Lazy-Instantiation: only-time INSTANCE created
    private volatile static Employee INSTANCE;
 
    // private constructor
    private Employee() {
        // helps to suppress creating objects either 
        // 1. by new operator/keyword or 
        // 2. by reflection API and newInstance() method
    }
 
    // static getInstance() method returning INSTANCE after check
    public static Employee getInstance() {
 
        if(null == INSTANCE){
            INSTANCE = new Employee();
        }
        return INSTANCE;
    }
 
    // to suppress creating new object during de-serialization
    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // directly throw Clone Not Supported Exception
        throw new CloneNotSupportedException();
    }
 
    // other utility methods and details of this class
}

 

3. Singleton class in a Multi-threaded environment:

Above article explains,

  • What is Singleton design pattern ?
  • Example for Singleton design pattern via eager-instantiation approach
  • Performance improvement by coding lazy-instantiation over eager-instantiation
  • How do handle singleton design pattern in a multi-threaded environment ?
  • Brief explanation about Double-Checked Locking pattern ?

 

Read Also:

 

References:

 

Happy Coding !!
Happy Learning !!

How to construct an immutable class in Java
Various ways to create Object in Java - 4 ways