Singleton
Pattern:
This is
useful in an application where we have to use just one instance of the class
and at any point of time more than one instance cannot be created for a class.
How to
create Singleton design pattern?
To create
the singleton class, we need to have static member of class, private
constructor and static factory method.
Static
member: It gets memory only once because of static, it contains the instance of
the Singleton class.
Private
constructor: It will prevent to instantiate the Singleton class from outside
the class.
Static
factory method: This provides the global point of access to the Singleton
object and returns the instance to the caller.
Code
Example 1:
package singleton;
public class Logger
{
private static Logger obj = new Logger();
private Logger() {}
public static Logger getLoggerObject()
{
return obj;
}
public void performLogging( String x)
{
// write into console
System.out.println("Error "+x);
}
}
package singleton;
public class SingletonExample
{
public static void main(String[] args)
{
Logger.getLoggerObject().performLogging("Test Singleton");
}
}
DisAdvantage: Object created before
its use.
Code Example 2: Lazy instantiation of singleton
design pattern
//Code Example
2: Lazy instantiation of singleton design pattern
package singleton2;
public class Logger
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
if(obj == null)
obj = new Logger();
return obj;
}
public void performLogging( String x)
{
// write into console
System.out.println("Error "+x);
}
}
package singleton2;
public class SingletonExample
{
public static void main(String[] args)
{
Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization");
}
}
DisAdvantage: Not thread safe.
Code Example3 : Thread safe Lazy instantiation of singleton
design pattern
// Code
Example3 : Thread safe Lazy instantiation of singleton design pattern
package singleton3;
public class Logger {
private static Logger obj;
private Logger() {}
public static synchronized Logger getLoggerObject()
{
if(obj == null)
obj = new Logger();
return obj;
}
public void performLogging( String x)
{
// write into
console
System.out.println("Error "+x);
}
}
// client calling
singleton class
package singleton3;
public class SingletonExample {
public static void main(String[] args)
{
Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization and thread safe");
}
}
DisAdvantage: Not
performant. Above implementation works fine and provides thread-safety but it
reduces the performance because of cost associated with the synchronized
method. The object creation need to be synchronized only for first few threads.
Code Example 4: Thread safe cum performant
singleton with double checked locking principle
// Code Example
4: Thread safe cum performant singleton with double checked
locking principle
package singleton4;
public class Logger
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
if(obj == null)
synchronized(Logger.class)
{
if(obj == null)
{
obj = new Logger();
}
}
return obj;
}
public void performLogging( String x)
{
// write into console
System.out.println("Error "+x);
}
}
package singleton4;
import singleton4.Logger;
public class SingletonExample
{
public static void main(String[] args)
{
Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization with thread safe and
performant");
}
}
Code Example 5: Avoiding breaking singleton
pattern using deserialization of object
If
singleton class is serializable, you can serialize singleton class object to a
file and deserialize it to get one more instance of the same class, to avoid
this override readResolve() method which will be called just after the
deserialization. In this method return singleton object as below code example:
package singleton5;
import java.io.Serializable;
public class Logger implements Serializable {
private static Logger obj;
private Logger() {}
public static Logger
getLoggerObject()
{
if(obj == null)
synchronized(Logger.class)
{
if(obj == null)
{
obj = new Logger();
}
}
return obj;
}
protected Object readResolve()
{
System.out.println("in readResolve
after deserialization");
return getLoggerObject();
}
public void performLogging( String x)
{
// write into
console
System.out.println("Error "+x);
}
}
package singleton5;
import java.io.*;
public class SingletonExample {
public static void main(String[] args) throws ClassNotFoundException
{
Logger test1,test2;
test1 = Logger.getLoggerObject();
test1.performLogging("Test Singleton
with lazy initialization with thread safe and performant");
// calling singleton
class with deserialization
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Logger.ser"));
out.writeObject(test1);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logger.ser"));
test2 =(Logger) in.readObject();
in.close();
test2.performLogging(" Testing with deserialization");
System.out.println("Hashcode of
instance with name test1:"+test1.hashCode());
System.out.println("Hashcode of
instance with name test2:"+test2.hashCode());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Code Example
6: Using reflection to break singleton.
Using
reflection we can break singleton design pattern, review the below code for
example. Here we read the constructors of singleton class using reflection and
create new instance to the singleton class which is different from actual
object returned by getLoggerObject() method
package singleton6;
import java.io.Serializable;
public class Logger implements Serializable {
private static Logger obj;
private Logger() {}
public static Logger
getLoggerObject()
{
if(obj == null)
synchronized(Logger.class)
{
if(obj == null)
{
obj = new Logger();
}
}
return obj;
}
protected Object readResolve()
{
System.out.println("in readResolve
after deserialization");
return getLoggerObject();
}
public void performLogging( String x)
{
// write into
console
System.out.println("Error "+x);
}
}
package singleton6;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SingletonExample {
public static void main(String[] args)
{
Logger obj1 = Logger.getLoggerObject();
obj1.performLogging("logging using obj1");
System.out.println("Hashcode of
obj1:"+obj1.hashCode());
// breaking singleton
using reflection
Logger obj2 = null;
try {
Constructor[] cons = Logger.class.getDeclaredConstructors();
for(Constructor constructor:cons)
{
constructor.setAccessible(true);
obj2 = (Logger) constructor.newInstance();
obj2.performLogging("logging using
obj2");
System.out.println("Hashcode of
obj2:"+obj2.hashCode());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Overcome
reflection issue:
To overcome issue raised by reflection, enums are used because java ensures
internally that enum value is instantiated only once. Since java Enums are
globally accessible, they can be used for singletons. Its only drawback is that
it is not flexible i.e it does not allow lazy initialization.
//Java program
for Enum type singleton
public enum Logger
{
INSTANCE;
}
Code
Example 7: Breaking Singleton using Cloning and how to avoid it
Java has
the ability to create a copy of object with similar attributes and state form
original object. This concept in java is called cloning. To implement cloning,
we have to implement java.lang.Cloneable interface and override clone() method
from Object class. It is a good idea to prevent cloning in a singleton class.
To prevent cloning on singleton object, let us explicitly throw
CloneNotSupportedException exception in clone() method.
// Code Example
7: Breaking Singleton using Cloning and how to avoid it
package singleton7;
import singleton7.Logger;
public class Logger implements Cloneable
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
if(obj == null)
synchronized(Logger.class)
{
if(obj == null)
{
obj = new Logger();
}
}
return obj;
}
public Object clone() throws CloneNotSupportedException
{
throw new CloneNotSupportedException();
}
public void performLogging( String x)
{
// write into console
System.out.println("Error "+x);
}
}
//Code Example
7: Breaking Singleton using Cloning and how to avoid it
package singleton7;
public class SingletonExample {
public static void main(String[] args)
{
Logger obj1 = Logger.getLoggerObject();
obj1.performLogging("logging using obj1");
System.out.println("Hashcode of
obj1:"+obj1.hashCode());
try
{
Logger obj2 = (Logger)obj1.clone();
}
catch (CloneNotSupportedException e)
{
// TODO Auto-generated catch block
System.out.println("clone failed due
to override of clone method"+e);
}
}
}
Here
above we have stopped user to create clone of singleton class. If you dont want
to throw exception you can also return the same instance of class from clone
method.
Code
Example 8 within JDK:
getRuntime()
method of java.lang.Runtime
The
java.lang.Runtime class allows the application to interface with the
environment in which the application is running. The
java.lang.Runtime.getRuntime() method returns the runtime object associated
with the current Java application which implements singleton pattern. Most of
the methods of class Runtime are instance methods and must be invoked with
respect to the current runtime object.
For
example availableProcessors() method returns the number of processors available
to the Java virtual machine. freeMemory() method of Runtime class returns the
amount of free memory in the Java Virtual Machine.
package singleton8;
public class SingletonExample {
public static void main(String[] args) {
// print when the program starts
System.out.println("Program starting...");
// get the current runtime singleton object associated
with this process
Runtime run1 = Runtime.getRuntime();
Runtime run2 = Runtime.getRuntime();
System.out.println("Hashcode of two objects of java.lang.Runtime class:: \nrun1:"+run1.hashCode()+"\nrun2:"+run2.hashCode());
// print the current free memory for this runtime
System.out.println("JVM free memory:" + run1.freeMemory());
System.out.println("JVM available processors:" + run1.availableProcessors());
System.out.println("JVM Max Memory:" + run1.maxMemory());
}
}
Note: If
singleton class is loaded by two class loaders then we can create two instances
of same singleton class will be created, one for each class loader.
Factory
Method Design Pattern
We have
super class and N number of sub classes, and based on the input data provided
we need to return object of one of the sub-classes, here we use Factory method
pattern.
Example
within JDK: java.util.Calendar # getInstance()
No comments:
Post a Comment