Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Singleton Design Pattern: Making Singleton More Effective in Java

DZone's Guide to

Singleton Design Pattern: Making Singleton More Effective in Java

Want to learn more about when to use the singleton design pattern in Java? Check out this post on the singleton pattern and its usage as a properties holder.

· Java Zone ·
Free Resource

Atomist automates your software deliver experience. It's how modern teams deliver modern software.

In a response to my previous article (How to Use Singleton Design Pattern in Java), I had a few important implementation points to point out.

A singleton design pattern simply means that we like to make sure that we have only one instance in the JVM. We know that in Java we can create an instance of a class by newclone, reflection, serializing/de-serializing, and container-provided. So, to make a class singleton, we have to perform the steps listed below: 

  • New: Change the constructor from private to prevent a new operator.

  • Clone: Implement java.lang.Clonable and override the clone() method to return the same singleton instance of the class.

  • Reflection: Implement the singleton pattern using  java-enum. However, this does not support lazy initialization.

public enum SingletonClass {
    SINGLE_INSTANCE;
}
  • Serializing/De-serializing: First, create the method  readReasolve() to return the same singleton instance of the class. The  readResolve() method is called when the ObjectInputStream is read as an object from the stream and is preparing to return it to the caller. ObjectInputStream checks whether the class of the object defines the readResolve method. If the method is defined, the  readResolve()  method is called to allow the object in the stream to designate the object to be returned. The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastException will be thrown when the type mismatch is discovered.

  • Container-Provided: This comes with a framework, like the SpringFramework, and we can control it up to an extent via the configuration.

In the below example, I have created a SingletonClass which is compatible with the Cloning and Serialization. I mean, The class will give the same single instance if we do clone() or writeObject(), readObject(). And if you dont want to have cloning and serialization support (which we do in 99% cases) into your SingletonClass, just simply not implement Cloneable and Serializable/Externilizable interfaces. That's it!

import java.io.Serializable;

class SingletonClass implements Cloneable, Serializable {

    private static final long serialVersionUID = -5664124213376258176L;

    private static SingletonClass SINGLE_INSTANCE = null;

    private SingletonClass() {}

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

    @Override protected Object clone() throws CloneNotSupportedException {
    return SINGLE_INSTANCE;
    }

    protected Object readResolve() {
        return SINGLE_INSTANCE;
    }
}

Singleton as a Properties Holder

I prefer to use the lazy initialization method for singleton as a properties holder. In this example, I have a  dbconnection.properties  that stores database connection-related values. I like to read and hold the values in the memory to use it wherever it needs.

# DB Connection Properties.
# Oracle DB properties
#jdbc.driver=oracle.jdbc.driver.OracleDriver
#jdbc.url=jdbc:oracle:thin:@localhost:1571:MyOracleDB
#jdbc.username=admin
#jdbc.password=admin

# MySQL DB properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/MySQLDB
jdbc.username=root
jdbc.password=admin

Below is the SingletonClass,which reads and keeps the database connection-related values. This class is dependent on the propertyfile and the other values. I am keeping the above property file in the classpath-folder or the root of the project folder. Hence, this path is not needed for this.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

class SingletonClass implements Cloneable, Serializable {

    private static final long serialVersionUID = 1007236647121448027L;

    private static final String CONNECTION_FILE_WITH_PATH = "dbconnection.properties";
    private static SingletonClass SINGLE_INSTANCE = null;

    private Properties properties = new Properties();

    private SingletonClass() {}

    public static SingletonClass getInstance() throws IOException {
        if (SINGLE_INSTANCE == null) {
            synchronized (SingletonClass.class) {
                if (SINGLE_INSTANCE == null) {
                InputStream file = new FileInputStream(new File(CONNECTION_FILE_WITH_PATH)) ;
                    SINGLE_INSTANCE = new SingletonClass();
                    SINGLE_INSTANCE.properties.load(file);
                }
            }
        }
        return SINGLE_INSTANCE;
    }

    @Override protected Object clone() throws CloneNotSupportedException {
    return SINGLE_INSTANCE;
    }

    protected Object readResolve() {
        return SINGLE_INSTANCE;
    }

    public String getPropertyValue(String propertyKey) {
      if (propertyKey != null && !propertyKey.isEmpty()) {
          return properties.getProperty(propertyKey);
      }
      return null;
    }

    public Map<String, String> getProperties() {
      Map<String, String> propertyMap = new HashMap<String, String>();
      for (String propertyKey : properties.stringPropertyNames()) {
      propertyMap.put(propertyKey, properties.getProperty(propertyKey));
      }
      return propertyMap;
    }

}

Here, in the  getProperties()  method, I have converted  java.util.Properties  to a  java.util.map  to prevent any modification of the loaded values.

Should We Use or Not Use Singleton?

If you search singleton, you will find lots of material claiming that the net stating singletons are bad, including statements like, "singletons are evil," and "it's anti-pattern." According to the masses, there are lots of issues with using singletons. So, should we use singleton or not?

Well, in my opinion, the answer is no. Until you are sure about what you are trying to achieve, for me, a singleton is something we like to only keep only in memory as an object. There is no use or wastage of memory if we duplicate them. I prefer to make them a property-values holder or an error-code supplier.

  • Please avoid making Controller , Connector  , and Managerkinds of classes as a singleton, since they may create a problem with duplicates. For example, this could happen if your application deployment goes into clustering behind any load-balancer.

  • You could also try using the monostate pattern as an alternative to the singleton. The definition I found is presented below:

"A monostate is a 'conceptual singleton' — all data members of a monostate are static, so all instances of the monostate use the same (static) data. Applications using a monostate can create any number of instances that they desire, as each instance uses the same data. What I find very nice about monostate classes is that they avoid all of the complications about having to access a particular instance of a class. Any instance is as good as another. "

Monostate Pattern

Below, we demonstrate one of the possible replacements for the above example using the monostate pattern.

class SingletonClassMono {

private static final String CONNECTION_FILE_WITH_PATH = "dbconnection.properties";

private static Properties properties = new Properties();

    static {
      InputStream file;
      try {
          file = new FileInputStream(new File(CONNECTION_FILE_WITH_PATH));
          properties.load(file);
      } catch (FileNotFoundException exp) {
          exp.printStackTrace();
      } catch (IOException exp) {
          exp.printStackTrace();
      }
    }

    public static String getPropertyValue(String propertyKey) {
      if (propertyKey != null && !propertyKey.isEmpty()) {
      return properties.getProperty(propertyKey);
      }
      return null;
    }

    public static Map<String, String> getProperties() {
      Map<String, String> propertyMap = new HashMap<String, String>();
      for (String propertyKey : properties.stringPropertyNames()) {
      propertyMap.put(propertyKey, properties.getProperty(propertyKey));
      }
      return propertyMap;
    }

}

In that case, instantiation of your class is not a problem anymore, and you don't have to worry about any of the scenarios you listed. I hope you enjoyed this post! Let me know what you think in the comments down below!

Liked the article? Don't forget to press that like button. Happy coding!

Need more articles on Design Patterns? Below are some of them I have shared with you.

Some additional Articles:

Get the open source Atomist Software Delivery Machine and start automating your delivery right there on your own laptop, today!

Topics:
java ,design pattern ,singleton ,properties holder ,code ,tutorial ,monostate pattern

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}