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.
Join the DZone community and get the full member experience.
Join For FreeIn 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 new, clone, 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 theclone()
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. ThereadResolve()
method is called when theObjectInputStream
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 thereadResolve
method. If the method is defined, thereadResolve()
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, aClassCastException
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
, andManager
kinds 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:
Opinions expressed by DZone contributors are their own.
Comments