What is prototype Pattern?

The Prototype pattern is a creation pattern based on cloning a pre-configured object. The idea is that you pick an object that is configured for either the default or in the ballpark of some specific use case and then you clone this object and configure to your exact needs.

When to use prototype Pattern?

  1. Prototype patterns are required when object creation is a time-consuming, and costly operation, so we create objects with the existing object itself.
  2. The Prototype Design Pattern allows you to create new objects by cloning existing ones without being bound to the specific classes of the objects being cloned. This promotes decoupling between the client code and the concrete classes, making it easier to introduce new types of objects without modifying the client code.

Where to use prototype Pattern?

  1. If you want to create an object but do not want to go through the expensive object creation procedure where network or database calls are made, then use the prototype pattern. Just create a copy of the object and do your changes on it.

How prototype Pattern is acheived?

  1. Create a Interface with clone method
  2. Create a Concrete prototypes which implements the interface. In the below example it is PersonPrototype. This Concrete class would be DB classes which consume lot of resource and the one which you dont want to create again and again
  3. Classes use the above Concrete prototype to add their own attributes and create new classes EmployeePrototype, StudentPrototype, SoftwareEmployee(Concrete Class)
  4. Prototypes are added to Registry which is nothing but an HashMap to Store String and Prototype classes
  5. Concrete classes which want to extend the Prototype uses the above hashmap registry to fetch the protype and add their custom attributes.

There are two things to note in code

  • One is Clone method which makes the extending class mandatory to implement clone method. The same can be done by implementing Clone interface
  • Second thing is how the Concrete Classes implements the clone and creates copy of itself at the same time calling the parent prototype class
    .
    .
        public StudentPrototype(StudentPrototype studentPrototype) {
            super(studentPrototype);
            this.institution = studentPrototype.institution;
            this.rollNo = studentPrototype.rollNo;
        }
    
    .
    
  • PersonPrototype is Concrete Prototype which is costly to create
  • We add Custom attributes at each level and create prototype and concrete class from PersonPrototype

Prototype.java

public interface Prototype<T> {
    T clone();
}

ConcretePersonPrototype.java

public class ConcretePersonPrototype implements Prototype<ConcretePersonPrototype>{
    private String name = "Mugilvannan G";
    private String aadhar = "1452-5874-5124-847";

    public ConcretePersonPrototype() {
    }

    @Override
    public ConcretePersonPrototype clone() {
        return new ConcretePersonPrototype(this);
    }

    public ConcretePersonPrototype(ConcretePersonPrototype concretePersonPrototype) {
        this.name = concretePersonPrototype.name;
        this.aadhar = concretePersonPrototype.aadhar;
    }

    public String getName() {
        return name;
    }

    public String getAadhar() {
        return aadhar;
    }
}

EmployeePrototype.java

public class EmployeePrototype extends ConcretePersonPrototype {
    private Integer pfAccountNo;
    private String startDate;
    private String endDate;

    public EmployeePrototype() {
    }

    public EmployeePrototype(EmployeePrototype employeePrototype) {
        super(employeePrototype);
        this.pfAccountNo = employeePrototype.pfAccountNo;
    }

    public EmployeePrototype clone() {
        return new EmployeePrototype(this);
    }

    public Integer getPfAccountNo() {
        return pfAccountNo;
    }

    public void setPfAccountNo(Integer pfAccountNo) {
        this.pfAccountNo = pfAccountNo;
    }

    public String getStartDate() {
        return startDate;
    }

    public void setStartDate(String startDate) {
        this.startDate = startDate;
    }

    public String getEndDate() {
        return endDate;
    }

    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }

    @Override
    public String toString() {
        return "Career Details : {" + '\'' +
                " Employee Name ='" + super.getName() + '\'' +
                " Employee Aadhar ='" + super.getAadhar() + '\'' +
                " Employee pfAccountNo='" + this.getPfAccountNo() + '\'' +
                '}';
    }
}

StudentPrototype.java

public class StudentPrototypeConcrete extends ConcretePersonPrototype {
    private String institution;
    private  Integer rollNo;

    public StudentPrototypeConcrete() {
    }

    public StudentPrototypeConcrete(StudentPrototypeConcrete studentPrototype) {
        super(studentPrototype);
        this.institution = studentPrototype.institution;
        this.rollNo = studentPrototype.rollNo;
    }

    public Integer getRollNo() {
        return rollNo;
    }

    public void setRollNo(Integer rollNo) {
        this.rollNo = rollNo;
    }

    public String getInstitution() {
        return institution;
    }

    public void setInstitution(String institution) {
        this.institution = institution;
    }

    public StudentPrototypeConcrete clone(){
        return new StudentPrototypeConcrete(this);
    }

    @Override
    public String toString() {
        return "Education Details : {" +
                " Student Name='" + super.getName() + '\'' +
                " Student Aadhar='" + this.getAadhar() + '\'' +
                " Student Institution='" + this.getInstitution() + '\'' +
                " Student Rollno='" + this.getRollNo() + '\'' +
                '}';
    }
}

EmployeePrototype.java

public class EmployeePrototype extends PersonPrototype {
    private Integer pfAccountNo;
    private String startDate;
    private String endDate;

    public EmployeePrototype() {
    }

    public EmployeePrototype(EmployeePrototype employeePrototype) {
        super(employeePrototype);
        this.pfAccountNo = employeePrototype.pfAccountNo;
    }

    public EmployeePrototype clone() {
        return new EmployeePrototype(this);
    }

    public Integer getPfAccountNo() {
        return pfAccountNo;
    }

    public void setPfAccountNo(Integer pfAccountNo) {
        this.pfAccountNo = pfAccountNo;
    }

    public String getStartDate() {
        return startDate;
    }

    public void setStartDate(String startDate) {
        this.startDate = startDate;
    }

    public String getEndDate() {
        return endDate;
    }

    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }

    @Override
    public String toString() {
        return "Career Details : {" + '\'' +
                " Employee Name ='" + super.getName() + '\'' +
                " Employee Aadhar ='" + super.getAadhar() + '\'' +
                " Employee pfAccountNo='" + this.getPfAccountNo() + '\'' +
                '}';
    }
}

SoftwareEmployee.java

public class SoftwareEmployee extends EmployeePrototype {
    public Integer empId;
    public String designation;
    public String practice;
    public String organisation;

    public SoftwareEmployee() {
    }

    public SoftwareEmployee(SoftwareEmployee softwareEmployee) {
        super(softwareEmployee);
        this.empId = softwareEmployee.empId;
        this.designation = softwareEmployee.designation;
        this.practice = softwareEmployee.practice;
        this.organisation = softwareEmployee.organisation;
    }

    public SoftwareEmployee clone(){
        return new SoftwareEmployee(this);
    }

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }

    public String getDesignation() {
        return designation;
    }

    public void setDesignation(String designation) {
        this.designation = designation;
    }

    public String getPractice() {
        return practice;
    }

    public void setPractice(String practice) {
        this.practice = practice;
    }

    public String getOrganisation() {
        return organisation;
    }

    public void setOrganisation(String organisation) {
        this.organisation = organisation;
    }

    @Override
    public String toString() {
        return "Present Work Details: {" +
                "Software Engineer Name ='" + super.getName() + '\'' +
                " Aadhar No ='" + super.getAadhar() + '\'' +
                " EmpId ='" + this.empId + '\'' +
                " Designation='" + this.designation + '\'' +
                " Organisation='" + this.organisation + '\'' +
                " Practice='" + this.practice + '\'' +
                '}';
    }
}

Client.java

public class Client {
    public static void fillRegistry(PersonRegistry registry) {
        StudentPrototype objStudPrototype = new StudentPrototype();
        objStudPrototype.setRollNo(15462);
        objStudPrototype.setInstitution("Mowbarys");
        registry.register("StudentPerson", objStudPrototype);

        EmployeePrototype objEmpPrototype = new EmployeePrototype();
        objEmpPrototype.setPfAccountNo(4151542);
        objEmpPrototype.setStartDate("2012");
        objEmpPrototype.setEndDate("NA");
        registry.register("EmployeePerson", objEmpPrototype);

        SoftwareEmployee objSoftwareEmployee = new SoftwareEmployee();
        objSoftwareEmployee.setPfAccountNo(4151542);
        registry.register("SoftwareEmployee", objSoftwareEmployee);
    }

    public static void main(String[] args) {
        PersonRegistry registry = new PersonRegistry();
        fillRegistry(registry);

        EmployeePrototype objEmployee = (EmployeePrototype) registry.get("EmployeePerson").clone();

        StudentPrototype objStudent = (StudentPrototype)registry.get("StudentPerson").clone();
        objStudent.setRollNo(15425);
        objStudent.setInstitution("Mowbarys");

        SoftwareEmployee objSoftEmployee1 = (SoftwareEmployee) registry.get("SoftwareEmployee").clone();
        objSoftEmployee1.setStartDate("2011");
        objSoftEmployee1.setEndDate("2012");
        objSoftEmployee1.setEmpId(1001);
        objSoftEmployee1.setOrganisation("Fuchsia Software");
        objSoftEmployee1.designation = "Programmer";
        objSoftEmployee1.practice = "Classic ASP, HTML, CSS";

        SoftwareEmployee objSoftEmployee2 = (SoftwareEmployee) registry.get("SoftwareEmployee").clone();
        objSoftEmployee2.setStartDate("2012");
        objSoftEmployee2.setEndDate("2015");
        objSoftEmployee2.setEmpId(754185);
        objSoftEmployee2.setOrganisation("Infosys");
        objSoftEmployee2.designation = "Technology Analyst";
        objSoftEmployee2.practice = "Java, Spring Boot";

        SoftwareEmployee objSoftEmployee3 = (SoftwareEmployee) registry.get("SoftwareEmployee").clone();
        objSoftEmployee3.setStartDate("2018");
        objSoftEmployee3.setEndDate("NA");
        objSoftEmployee3.setEmpId(152);
        objSoftEmployee3.setOrganisation("Cognizant");
        objSoftEmployee3.designation = "Technology Lead";
        objSoftEmployee3.practice = "Java, Devops";


        System.out.println(objStudent);
        System.out.println(objEmployee);
        System.out.println(objSoftEmployee1);
        System.out.println(objSoftEmployee2);
        System.out.println(objSoftEmployee3);
    }
}

Output

Education Details : { Student Name='Mugilvannan G' Student Aadhar='1452-5874-5124-847' Student Institution='Mowbarys' Student Rollno='15425'}
Career Details : {' Employee Name ='Mugilvannan G' Employee Aadhar ='1452-5874-5124-847' Employee pfAccountNo='4151542'}
Present Work Details: {Software Engineer Name ='Mugilvannan G' Aadhar No ='1452-5874-5124-847' EmpId ='1001' Designation='Programmer' Organisation='Fuchsia Software' Practice='Classic ASP, HTML, CSS'}
Present Work Details: {Software Engineer Name ='Mugilvannan G' Aadhar No ='1452-5874-5124-847' EmpId ='754185' Designation='Technology Analyst' Organisation='Infosys' Practice='Java, Spring Boot'}
Present Work Details: {Software Engineer Name ='Mugilvannan G' Aadhar No ='1452-5874-5124-847' EmpId ='152' Designation='Technology Lead' Organisation='Cognizant' Practice='Java, Devops'}

Why builder Pattern?
If there are many attributes in the class and object is created over several calls it may be in an inconsistent state partway through its construction. To address this builder pattern is introduced. If Object Creation should be delayed until all the validation of class attributes are performed builder pattern addresses the need.

If there are 2 attributes to a Class then we can create 2^N Constructor based on the two attributes. This Results in Boiler plate code. What if the number of attributes keeps increasing over a period of time.

Report.java

import java.util.InvalidPropertiesFormatException;

public class Report {
    private String reportName;
    private String reportFormat;

    private Report(){}

    public static ReportBuilder getReportBuilder(){
        return new ReportBuilder();
    }


    @Override
    public String toString() {
        return "Report{" +
                "reportName='" + reportName + '\'' +
                ", reportFormat='" + reportFormat + '\'' +
                '}';
    }

    public static class ReportBuilder {
        private String reportName;
        private String reportFormat;

        private ReportBuilder(){}

        public ReportBuilder setReportName(String reportName) {
            this.reportName = reportName;
            return this;
        }

        public ReportBuilder setReportFormat(String reportFormat) {
            this.reportFormat = reportFormat;
            return this;
        }

        public Report build() throws InvalidPropertiesFormatException {
            if(this.reportFormat == "CSV"){
                throw new InvalidPropertiesFormatException("Format Not Supported");
            }

            if(this.reportName.length() < 10){
                throw new InvalidPropertiesFormatException("Name should be greater than 10");
            }

            //Object for Report should not be created before validation is completed as above
            Report objReport = new Report();
            objReport.reportName = this.reportName;
            objReport.reportFormat = this.reportFormat;
            return objReport;
        }
    }
}

BuilderPatternClient.java

import java.util.InvalidPropertiesFormatException;

public class BuilderPatternClient {
    public static void main(String[] args) throws InvalidPropertiesFormatException {
        Report objReport = Report.getReportBuilder()
                .setReportFormat("PDF")
                .setReportName("Monthly Transactions")
                .build();

        System.out.println(objReport);
    }
}

Output

Report{reportName='Monthly Transactions', reportFormat='PDF'}

How it Works

  1. We have JSON file with Location and Name of Factory Class
  2. Using Configuration.java we read the JSON File and read the Configuration
  3. We run TasteFoodFromMenu.java by supplying the JSON file Location as Input
  4. Now by the above method the JAR’s could be built independently and by changing the JSON file we could make changes and deploy the code without restarting the Server

For more detail refer here

Configuration.java

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;

import com.fasterxml.jackson.databind.ObjectMapper;

public class Configuration {

    public static Configuration loadConfiguration(String fileName) throws IOException {    	
    	//Read Name of File
        Path path = FileSystems.getDefault().getPath(fileName);
        
        //Read Contents of File
        String contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
        
        ObjectMapper mapper = new ObjectMapper();
        
        //Map Location and FactoryType
        Configuration config = mapper.readValue(contents, Configuration.class);
        return config;
    }

    private String factoryType;
    private String location;


    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public String getFactoryType() {
        return factoryType;
    }

    public void setFactoryType(String factoryType) {
        this.factoryType = factoryType;
    }
}

TasteFoodFromMenu.java

public class TasteFoodFromMenu {
	  public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
	        Configuration configuration = Configuration.loadConfiguration(args[0]);
	        String location = configuration.getLocation();
	        URL url = new URL(location);
	        URLClassLoader ucl = new URLClassLoader(new URL[]{url});
	        Class<IFoodFactory> cls = (Class<IFoodFactory>) Class.forName(configuration.getFactoryType(), true, ucl);
	        IFoodFactory cameraFactory = cls.newInstance();
	        IFood camera = cameraFactory.prepareFood(); 
	        camera.serveFood();
	    }
}

config.json

{
  "factoryType": "com.mugil.food.ChineseFoodFactory",
  "location": "file:///D:/java/ReadConfFromJSON/lib/FoodMenu.jar"
}

Abstract Factory Pattern
The Abstract Factory Pattern consists of an AbstractFactory, ConcreteFactory, AbstractProduct, ConcreteProduct and Client. We code against AbstractFactory and AbstractProduct and let the implementation to others to define ConcreteFactory and ConcreteProduct

Why Abstract Factory Introduced?
Another layer of abstraction over factory pattern. Abstract Factory patterns work around a super-factory which creates other factories. Factory pattern deals with creating objects of a single type(AppleFactory manufactures Iphones), while the Abstract Factory pattern deals with creating objects of related types(MobileFactory manufactures Mobiles). Abstract Factory pattern is more robust and consistent and uses composition to delegate responsibility of object creation.

When to use Abstract Factory?
When higher level of Abstraction is required.Provides an interface for creating families of related objects

MobileManufactureFactory.java

public interface MobileManufactureFactory {
    public Mobiles getManufacturerDetails();
}

Mobiles.java

public interface Mobiles {
    public void getMobileModels();
}

AppleFactory.java

public class AppleFactory implements MobileManufactureFactory {
    @Override
    public Mobiles getManufacturerDetails() {
        return new Iphone();
    }
}

GoogleFactory.java

public class GoogleFactory implements MobileManufactureFactory {
    @Override
    public Mobiles getManufacturerDetails() {
        return new Android();
    }
}

MicrosoftFactory.java

public class MicrosoftFactory implements MobileManufactureFactory {
    @Override
    public Mobiles getManufacturerDetails() {
        return new Windows();
    }
}

IphoneMobiles.java

public class IphoneMobiles implements Mobiles {
    @Override
    public void getMobileModels() {
        System.out.println("Iphone 13,14 and 15");
    }
}

AndroidMobiles.java

public class AndroidMobiles implements Mobiles {
    @Override
    public void getMobileModels() {
        System.out.println("Oneplus, Realme, Samsung");
    }
}

WindowsMobile.java

public class WindowsMobile implements Mobiles {
    @Override
    public void getMobileModels() {
        System.out.println("Samsung Focus, Nokia Lumia, Pocket PC");
    }
}

MobileFactory.java

public class MobileFactory {
    private MobileFactory(){
    }

    public static MobileManufactureFactory getMobilesBasedOnManufacturer(MobileCompany manufacturerName){
        if(manufacturerName.equals(MobileCompany.APPLE)){
            return new AppleFactory();
        }else if(manufacturerName.equals(MobileCompany.MICROSOFT)){
            return new MicrosoftFactory();
        }else if(manufacturerName.equals(MobileCompany.GOOGLE)){
            return new GoogleFactory();
        }
        return null;
    }
}

MobileCompany.java

public enum MobileCompany {
    APPLE, MICROSOFT, GOOGLE
}

Client.java

public class Client {
    public static void main(String[] args) {
        MobileManufactureFactory objMobileManufa = MobileFactory.getMobilesBasedOnManufacturer(MobileCompany.APPLE);
        Mobiles objMobile = objMobileManufa.getManufacturerDetails();
        objMobile.getMobileModels();
    }
}

Output

 Iphone 13,14 and 15

While Implementing Singleton the following things should be answered

  1. Reflection
  2. Serialization
  3. Cloning

Objects for Singleton Classes implemented using private Constructor can be invoked by Reflection as below

Item3.java

package com.mugil.org.ej;

import java.lang.reflect.Constructor;

public class Item3 {
	public static void main(String[] args) 
	{
		// reflection concept to get constructor of a Singleton class.  
		Constructor<Singleton> constructor;
		
		try {			
			constructor = Singleton.class.getDeclaredConstructor();
			
			// change the accessibility of constructor for outside a class object creation.
			constructor.setAccessible(true);
			
			// creates object of a class as constructor is accessible now.
			Singleton secondOb = constructor.newInstance();
			System.out.println(secondOb.getName());
			
			// close the accessibility of a constructor.
			constructor.setAccessible(false);
		} catch (Exception e){
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
}


class Singleton {

    private static Singleton instance = new Singleton();

    /* private constructor */
    private Singleton() {}

    public static Singleton getDefaultInstance() {
        return instance;
    }
    
    public String getName()
    {
    	return "MyName";
    }
}

Output

MyName

Singleton and Serialization
Without readResolve() Method

// Java code to explain effect of 
// Serilization on singleton classes
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
class Singleton implements Serializable 
{
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
     
    private Singleton() 
    {
        // private constructor
    }
}
 
 
public class GFG 
{
 
    public static void main(String[] args) 
    {
        try
        {
            Singleton instance1 = Singleton.instance;
            ObjectOutput out
                = new ObjectOutputStream(new FileOutputStream("file.text"));
            out.writeObject(instance1);
            out.close();
     
            // deserailize from file to object
            ObjectInput in 
                = new ObjectInputStream(new FileInputStream("file.text"));
             
            Singleton instance2 = (Singleton) in.readObject();
            in.close();
     
            System.out.println("instance1 hashCode:- "
                                                 + instance1.hashCode());
            System.out.println("instance2 hashCode:- "
                                                 + instance2.hashCode());
        } 
         
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }
}

Output

instance1 hashCode:- 1550089733
instance2 hashCode:- 785945

With readResolve() Method

// Java code to remove the effect of 
// Serialization on singleton classes
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
class Singleton implements Serializable 
{
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
     
    private Singleton() 
    {
        // private constructor
    }
     
    // implement readResolve method
    protected Object readResolve()
    {
        return instance;
    }
}
 
public class GFG 
{
 
    public static void main(String[] args) 
    {
        try
        {
            Singleton instance1 = Singleton.instance;
            ObjectOutput out 
                = new ObjectOutputStream(new FileOutputStream("file.text"));
            out.writeObject(instance1);
            out.close();
         
            // deserailize from file to object
            ObjectInput in 
                = new ObjectInputStream(new FileInputStream("file.text"));
            Singleton instance2 = (Singleton) in.readObject();
            in.close();
         
            System.out.println("instance1 hashCode:- "
                                           + instance1.hashCode());
            System.out.println("instance2 hashCode:- "
                                           + instance2.hashCode());
        } 
         
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Output

instance1 hashCode:- 1550089733
instance2 hashCode:- 1550089733

Refer https://codethataint.com/blog/singleton-and-serialization/

// JAVA code to explain cloning 
// issue with singleton
class SuperClass implements Cloneable
{
  int i = 10;
 
  @Override
  protected Object clone() throws CloneNotSupportedException 
  {
    return super.clone();
  }
}
 
// Singleton class
class Singleton extends SuperClass
{
  // public instance initialized when loading the class
  public static Singleton instance = new Singleton();
 
  private Singleton() 
  {
    // private constructor
  }
}
 
public class GFG
{
  public static void main(String[] args) throws CloneNotSupportedException 
  {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = (Singleton) instance1.clone();
    System.out.println("instance1 hashCode:- "
                           + instance1.hashCode());
    System.out.println("instance2 hashCode:- "
                           + instance2.hashCode()); 
  }
}

Output

Output :- 
instance1 hashCode:- 366712642
instance2 hashCode:- 1829164700

Two different hashCode means there are 2 different objects of singleton class.

To overcome this issue, override clone() method and throw an exception from clone method that is CloneNotSupportedException. Now whenever user will try to create clone of singleton object, it will throw exception and hence our class remains singleton.

// JAVA code to explain overcome 
// cloning issue with singleton
class SuperClass implements Cloneable
{
  int i = 10;
 
  @Override
  protected Object clone() throws CloneNotSupportedException 
  {
    return super.clone();
  }
}
 
// Singleton class
class Singleton extends SuperClass
{
  // public instance initialized when loading the class
  public static Singleton instance = new Singleton();
 
  private Singleton() 
  {
    // private constructor
  }
 
  @Override
  protected Object clone() throws CloneNotSupportedException 
  {
    throw new CloneNotSupportedException();
  }
}
 
public class GFG
{
  public static void main(String[] args) throws CloneNotSupportedException 
  {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = (Singleton) instance1.clone();
    System.out.println("instance1 hashCode:- "
                         + instance1.hashCode());
    System.out.println("instance2 hashCode:- "
                         + instance2.hashCode()); 
  }
}

Output

Output:-
Exception in thread "main" java.lang.CloneNotSupportedException
	at GFG.Singleton.clone(GFG.java:29)
	at GFG.GFG.main(GFG.java:38)

If you don;t want to throw exception you can also return the same instance from clone method.

// JAVA code to explain overcome 
// cloning issue with singleton
class SuperClass implements Cloneable
{
  int i = 10;
 
  @Override
  protected Object clone() throws CloneNotSupportedException 
  {
    return super.clone();
  }
}
 
// Singleton class
class Singleton extends SuperClass
{
  // public instance initialized when loading the class
  public static Singleton instance = new Singleton();
 
  private Singleton() 
  {
    // private constructor
  }
 
  @Override
  protected Object clone() throws CloneNotSupportedException 
  {
    return instance;
  }
}
 
public class GFG
{
  public static void main(String[] args) throws CloneNotSupportedException 
  {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = (Singleton) instance1.clone();
    System.out.println("instance1 hashCode:- "
                           + instance1.hashCode());
    System.out.println("instance2 hashCode:- "
                           + instance2.hashCode()); 
  }
}

Output

Output:-
instance1 hashCode:- 366712642
instance2 hashCode:- 366712642

The Best way to implement Singleton is by using ENUM which takes care of Serialization and Other Issues on its own.

Singleton.java

public class Singleton 
{ 
    private static Singleton instance;   
 
    private Singleton() {}

    public static Singleton getInstance() 
    {
       if (instance == null) {
          instance = new Singleton();
       }
       return instance;
    }
}

In a Multi-threaded Environment

  1. Two Threads, Thread A and Thread B tries to access Object of Singleton class
  2. Thread A and B does call to Static getInstance() method
  3. Now when Thread A tries to do Null Check of instance instance == null chances of Thread B also entered the if block exists
    .
    .
    .
    if (instance == null) 
    {
      //Two threads may have passed the condition and might have got in
      instance = new Singleton();
    }
    .
    .
    .
    
  4. Now both the Threads have their own instance for the Singleton class.
  5. So there would be 2 instances of Singleton class at the end of if Block

Now there are many work arounds but DoubleCheckedLocking, Enum Singleton and Initialization On Demand Holder are the Optimized approach

Refer Table in Link

Method 1
In this method we does static block initialization of Singleton along with synchronized getInstance() method which allows the access to only one thread at a point of time.
Singleton.java

public class Singleton 
{
    private static Singleton instance;
 
    private Singleton() {}

    public static synchronized Singleton getInstance() 
    {
       if (instance == null) {
          instance = new Singleton();
       }
       return instance;
    }
}

But the above method is expensive since there is unnecessary locking and unlocking done every time the object get accessed.

Method 2(Double Checked Locking Singleton)
Instead of synchronizing the whole method lets synchronize the block of code which allows single thread to access the instance during the first time access.The consecutive thread would be served with the same thread allocated for the First thread once it is done with its task.

Singleton.java

public class Singleton 
{
    private volatile static Singleton instance;

    private Singleton() {}

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

Other ways of achieving singleton are by using Eager Initialization and ENUM which has its own advantages and disadvantages.

In which scenario we should serialize a singleton?
Imagine you have a long-running app and want to be able to shut it down and later continue at the point where it was shut down (e.g. in order to do hardware maintenance). If the app uses a singleton that is stateful, you’d have to be able to save and restore the sigleton’s state, which is most easily done by serializing it.

Is it possible to serialize a singleton object?
2 Methods

  1. By using ENUM : ENUM implements Serializable by Default
  2. By adding implements Serializable to class to make it serializable, and delaring all instance fields transient (to prevent a serialization attack) and provide a readResolve method.

readResolve() method in Singleton Class implementing Serialization

.
.
// readResolve method to preserve singleton property
private Object readResolve() {
     // Return the one true Elvis and let the garbage collector
     // take care of the Elvis impersonator.
    return INSTANCE;
}
.
.

Why you need readResolve() Method
This method will be invoked when you will de-serialize the object. Inside this method, you must return the existing instance to ensure single instance application wide.

The Way serialization works is as below

Serializes the Object Property -> Stores to Persistent Storage
From Persistent Storage -> Creates new Object and Sets Properties of Object

The Object Before Serialization and after Serialization are not same. Only the Object Properties are same

Now lets take a simple Example where we serializes the PrintReport Class which has priority as one of its Object Variable.priority tells which should be given first preference while printing

PrintReport.java

package com.mugil.org;

import java.io.Serializable;

public class PrintReport implements Serializable {
    private static PrintReport instance = null;
	 
    public static PrintReport getInstance() {
        if (instance == null) {
            instance = new PrintReport();
        }
        return instance;
    }
    
    private transient int priority = 2;

	public int getPriority() {
		return priority;
	}
	
	public void setPriority(int priority) {
		this.priority = priority;
	}
}

Now when the above singleton which implements serialization gets called as below

PrintMedicalReport.java

package com.mugil.org;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class PrintMedicalReport 
{	
	 static PrintReport instanceOne = PrintReport.getInstance();
	 
	    public static void main(String[] args) {
	        try {
	            // Serialize to a file
	            ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
	                    "filename.ser"));
	            out.writeObject(instanceOne);
	            out.close();
	            
	            instanceOne.setPriority(1);
	            
	            // Serialize to a file
	            ObjectInput in = new ObjectInputStream(new FileInputStream(
	                    "filename.ser"));
	            PrintReport instanceTwo = (PrintReport) in.readObject();
	            in.close();
	 
	            System.out.println(instanceOne.getPriority());
	            System.out.println(instanceTwo.getPriority());
	 
	        } catch (IOException e) {
	            e.printStackTrace();
	        } catch (ClassNotFoundException e) {
	            e.printStackTrace();
	        }
	    }
}

Output

1
2

Note in the above code the instanceOne.setPriority(2); should have been called before out.writeObject(instanceOne).But that is not the case since the example explains the Object are different before and after Serialization.Only Object Properties(Metadata) are stored during serialization not the actual object.

You can see the Class Prints default Priority value in the output.

Now in context to singleton we want to maintain exactly one Object exist before and after serialization. Not with two object with same set of attributes.To achieve this we add readResolve() Method.

PrintReport.java

package com.mugil.org;

import java.io.Serializable;

public class PrintReport implements Serializable {
	private volatile static PrintReport instance = null;
	 
    public static PrintReport getInstance() {
        if (instance == null) {
            instance = new PrintReport();
        }
        return instance;
    }
    
    private int priority = 1;

	public int getPriority() {
		return priority;
	}
	
	public void setPriority(int priority) {
		this.priority = priority;
	}    
	     
    protected Object readResolve() {
        return instance;
    }   
}

Now when the PrintMedicalReport class gets executed the Output would be as below

Output

1
1

When you Serialize and Deserialize new file would be created and stored in disk every time.

In the above image by changing the filename.ser text you can change the attribute of objects serialized. This is known as Serialization attack. To overcome this you must declare all instance fields as transient.

Reference 2

Lets Consider the below Singleton code which uses Double Checked Locking

DoubleCheckLocking.java

public class DoubleCheckLocking 
{
    public static class SearchBox 
    {
        private static volatile SearchBox searchBox;

        //Private constructor
        private SearchBox() {}

        //Static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

Lets dive deep into the code where Double Checking actually takes place

  if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
  1. Lets say we have two threads A and B and lets assume that atleast one of them reaches line 3 and observes searchBox == null is true.
  2. Two threads can not both be at line 3 at the same time because of the synchronized block. This is the key to understanding why double-checked locking works.
  3. So, it must the case that either A or B made it through synchronized first. Without loss of generality, say that that thread is A. Then, upon seeing searchBox == null is true, it will enter the body of the statement, and set searchBox to a new instance of SearchBox. It will then eventually exit the synchronized block.
  4. Now it will be B’s turn to enter: remember, B was blocked waiting for A to exit. Now when it enters the block, it will observe searchBox. But A will have left just having set searchBox to a non-null value.

Simple Singleton Using ENUM

MySingleton.java

public enum MySingleton {
  INSTANCE;   
}

Enum Classes has Private Constructor by Default

The Above code can be explicitly written as

MySingleton.java

public enum MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton() {
    }
}

When your code first accesses INSTANCE, the class MySingleton will be loaded and initialized by the JVM. This process initializes the static field above once (lazily).

Why cant enum constructors be protected or public in Java?
Enums as a class with a finite number of instances. There can never be any different instances beside the ones you initially declare.Thus, you cannot have a public or protected constructor, because that would allow more instances to be created.

When Singleton should be used?
Singleton should be used incase creation of Object is costly or heavy I.E. DBConnection. It should not be used if the Object is mutable.

  1. Eager initialization
  2. Lazy initialization
  3. Static block initialization
  4. Using Synchronized method
  5. Using Synchronized block
  6. Double Checked Locking
  7. Enum Singleton
  8. Initialization on demand holder idiom

Eager initialization
An instance of Singleton Class is created at the time of class loading

EagerInitialized.java

package com.mugil.singleton;

public class EagerInitialized {
	
	private static final EagerInitialized instance = new EagerInitialized();
	
	//Private Constructor
	private EagerInitialized()
	{
	}
	
	public static EagerInitialized getInstance()
	{
         return instance;
        }	
}

Lazy Initialization
With lazy initialization you crate instance only when its needed and not when the class is loaded

LazyInitialized.java

package com.mugil.singleton;

public class LazyInitialized 
{
	public static  LazyInitialized instance = null; 
	
	private LazyInitialized()
	{
		
	}
	
	public static LazyInitialized getInstance(){
        if(instance == null){
            instance = new LazyInitialized();
        }
        return instance;
    }
}

Static Block Initialization
Static block initialization is similar to eager initialization, except that an instance of class is created in the static block that provides an option for exception handling.

StaticBlockInitialized.java

package com.mugil.singleton;

public class StaticBlockInitialized {

	private static StaticBlockInitialized instance;

	private StaticBlockInitialized() {
	}

	// Static block initialization for exception handling
	static {
		try {
			instance = new StaticBlockInitialized();
		} catch (Exception e) {
			throw new RuntimeException("Exception occurred in creating singleton instance");
		}
	}

	public static StaticBlockInitialized getInstance() {
		return instance;
	}
}

Using Synchronized Method
The easier way to create a thread-safe singleton class is to make the access method synchronized, so that only one thread can execute this method at a time.

ThreadSafeSing.java

package com.mugil.singleton;

public class ThreadSafeSing {

    private static ThreadSafeSinginstance;
    
    private ThreadSafeSing(){}
    
    public static synchronized ThreadSafeSing getInstance(){
        if(instance == null){
            instance = new ThreadSafeSing();
        }
        return instance;
    }    
}

Using Synchronized Block
The easier way to create a thread-safe singleton class is to make the access method synchronized, so that only one thread can execute this method at a time.

ThreadSafeSing.java

package com.mugil.singleton;

public class ThreadSafeSing {
    private static ThreadSafeSinginstance;
    
    private ThreadSafeSing(){}
    
    public static ThreadSafeSing getInstance(){
        Synchronized(ThreadSafeSing.class){ 
          if(instance == null){
            instance = new ThreadSafeSing();
          }
        }
        return instance;
    }    
}

Double Checked Locking Singleton
Above Implementation of Singleton provides thread safety buy has a hit over performance.Refer the link for the same Link

In the Below method of Implementing Singleton, Synchronized Block is used rather than Synchronized Method.
DCCSingleton.java

package com.mugil.singleton;

public class DCCSingleton{

    private static DCCSingletoninstance;
    
    private DCCSingleton(){}
    
   public static DCCSingleton getInstanceUsingDoubleLocking()
   {
    if(instance == null){
        synchronized (DCCSingleton.class) {
            if(instance == null){
                instance = new DCCSingleton();
            }
        }
    }
    return instance;
  }    
}

Refer the Link for how Double Checked Locking works

ENUM Singleton
enum fields are compile time constants, but they are instances of their enum type. And, they’re constructed when the enum type is referenced for the first time.
ENUMSingleton.java
Simple Singleton using enum for DBConnection

enum Singleton
{
    INSTANCE;

    // instance vars, constructor
    private final Connection connection;

    private Singleton()
    {
        //Initialize the connection
        connection = DB.getConnection();
    }

    // Static getter
    public static Singleton getInstance()
    {
        return INSTANCE;
    }

    public Connection getConnection()
    {
        return connection;
    }

The Connection can be created using the Below Code

Connection Conn = Singleton.getInstance().getConnection();

Initialization on demand holder idiom
How it works?

  1. The Below implementation is a lazy loading and thread safe method of creating Singleton
  2. On calling the getInstance() method the lazySingletonClass would be initialized which in turn makes the INSTANCE to be initialized.
  3. Class initialization is inherently thread-safe and if you can have an object initialized on class initialization the object creation too are thread-safe.
  4. Singleton instance variable will never be created and or initialized until getInstance() is invoked. And again since class initialization is thread-safe the instance variable of IntiailizationOnDemandClassholder will be loaded safely, once and is visible to all threads.
public class Singleton {
    private Singleton(){}

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

    public Singleton getInstance(){
        return lazySingletonClass.INSTANCE;
    }
}
When Object is Created Thread Safe Performance Comments
Eager Initialization Object Gets Created once the Class is Loaded Yes Bad Object is created everytime when the class is accessed
Lazy Initialization Object is Created once required No In lazy initialization you give a public API to get the instance. In multi-threaded environment it poses challenges to avoid unnecessary object creation
Static Block Initialization Object is Created once Static Block is loaded Yes Bad Not a good idea to load resources from static block as it causes performance hit during app startup
Synchronized Method Object is Created once getInstance is called Yes Bad Thread safety is guaranteed.Slow performance because of whole method locking is done
Synchronized Block Object is Created once getInstance is called No Two singleton instances may be created when context switching happens after checking instance is null
Double Checked Locking Object is Created once getInstance is called Yes Good Instance check is done twice
Enum Implementation Object Created using enum is referenced Yes Good
Initialization on demand holder idiom Object Created when static class is initiazlied by calling getInstance method Yes Good Class initialization is inherently thread-safe and if you can have an object initialized on class initialization the object creation too are thread-safe

Note
Dont be confused between Class Loading and Initialization. In the Initialization on demand holder method of singleton you can easily get confused as the static inner class wont be loaded when the outer class loads. The implementation relies on the fact that during initialization phase of execution within the Java Virtual Machine (JVM) as specified by the Java Language Specification (JLS) When the class Singleton is loaded by the JVM, the class goes through initialization. Since the class does not have any static variables to initialize, the initialization completes trivially. The static class definition lazySingletonClasswithin it is not initialized until the JVM determines that lazySingletonClassmust be executed. The static class lazySingletonClassis only executed when the static method getInstance is invoked on the class

The static class lazySingletonClass is only executed when the static method getInstance is invoked on the class Singleton , and the first time this happens the JVM will load and initialize the lazySingletonClass class. The initialization of the lazySingletonClassclass results in static variable INSTANCE being initialized by executing the (private) constructor for the outer class Something. Since the class initialization phase is guaranteed by the JLS to be sequential, i.e., non-concurrent, no further synchronization is required in the static getInstance method during loading and initialization