Java | Singleton Pattern
“Enforce the singleton property with private constructor or an enum type” ~ Joshua Block’s, Effective Java
Some objects are required to be singleton such as “builder, facade, state, logging, …etc”. Singleton pattern can be used to enforce the creation of exactly one instance of those objects.
How to Enforce the Creation of a Single Instance?
There are two common way to implement singleton:
1. private
Constructor with public static
member
- The
private
constructor ensures that no calls will be allowed from outside the class. - The
private
constructor can be further protected by throwing an exception if a second instance is requested. - The
public
member in this way can be either a static field or static method.public
feild approach is preferable overpublic
factory method.public
factory method approach can be more flexible e.g. by allowing the return of an instance for each thread if necessary.
- 1.1 pubic field
public class Universe {
public static final Universe INSTANCE = new Universe();
private long timestamp;
private Universe() {
timestamp = System.currentTimeMillis();
}
}
- 1.2 public static factory method
public class Universe {
private static final Universe INSTANCE = new Universe();
private long timestamp;
private Universe() {
timestamp = System.currentTimeMillis();
}
public static Universe getInstance() {
return INSTANCE;
}
}
Dealing with serialization
- Maintaining singleton is not guaranteed for classes implementing
Serializable
. - New instances will be created when serialized instances are deserialized.
public class Universe implements Serializable {
public static final Universe INSTANCE = new Universe();
private long timestamp;
private Universe() {
timestamp = System.currentTimeMillis();
}
}
public class App {
public static void main(String[] args) throws Exception {
Universe universe = Universe.INSTANCE;
// serialize and deserialize
Serializer.serialize(universe);
Universe deserilizedUniverse = Serializer.deserialize();
System.out.println(deserilizedUniverse == universe); // --> false
}
}
class Serializer{
public static void serialize(Universe universe) throws Exception {
FileOutputStream file = new FileOutputStream("output.txt");
ObjectOutputStream out = new ObjectOutputStream(file);
out.writeObject(universe);
out.close();
file.close();
}
public static Universe deserialize() throws Exception {
FileInputStream file = new FileInputStream("output.txt");
ObjectInputStream in = new ObjectInputStream(file);
Universe universe = (Universe) in.readObject();
in.close();
file.close();
return universe;
}
}
- To overcome this issue we need to add the following
- Declare all instance feilds
transient
- Implement readResolve() method
- readResolve() is called when an object is deserialized.
- A class implementing the readResolve() method, can directly control the return of the deserialization process.
- So will use this trick to replace deserialized object with the original “INSTANCE” object.
- Declare all instance feilds
public class Universe implements Serializable {
public static final Universe INSTANCE = new Universe();
private transient long timestamp;
private Universe() {
timestamp = System.currentTimeMillis();
}
private Object readResolve() {
return INSTANCE; //not deserialized one!
}
}
System.out.println(deserilizedUniverse == universe); // --> true
2. Single Element enum
- Single-element
enum
is similar to public field approach. - Single-element
enum
is thread, serialization and reflection safe approach.
”..a single-element enum type is often the best way to implement a singleton.” ~ Joshua Block’s, Effective Java
public enum Universe {
INSTANCE(System.currentTimeMillis());
private long timestamp;
private Universe(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return timestamp;
}
}
public class App {
public static void main(String[] args) throws Exception {
System.out.println(Universe.INSTANCE.getTimestamp());
}
}