Understanding the Singleton Pattern: Definition, Nine Implementations, Pros & Cons, and Android Applications
This article explains the Singleton design pattern, its core concept of ensuring a single instance, presents nine Java implementations with code examples, compares their advantages and disadvantages, discusses real‑world usage in Android and general development, and offers guidelines for when to apply the pattern.
Singleton (Singleton Pattern) is an object‑creation design pattern that guarantees a class has only one instance and provides a global access point to it.
Key characteristics:
Instance uniqueness
Self‑creation
Global access
Below are nine common Java implementations.
Eager Initialization (static constant)
/**
* Eager (static constant)
*/
class King {
private static final King kingInstance = new King();
private King() {}
static King getInstance() { return kingInstance; }
}Eager Initialization (static block)
/**
* Eager (static block)
*/
class King {
private static King kingInstance;
static { kingInstance = new King(); }
private King() {}
public static King getKingInstance() { return kingInstance; }
}Lazy Initialization (thread‑unsafe)
/**
* Lazy (thread‑unsafe)
*/
public class King {
private static King kingInstance;
private King() {}
public static King getKingInstance() {
if (kingInstance == null) {
kingInstance = new King();
}
return kingInstance;
}
}Lazy Initialization (synchronized method)
public class King {
private static King kingInstance;
private King() {}
public static synchronized King getKingInstance() {
if (kingInstance == null) {
kingInstance = new King();
}
return kingInstance;
}
}Lazy Initialization (synchronized block)
public class King {
private static King kingInstance;
private King() {}
public static King getKingInstance() {
if (kingInstance == null) {
synchronized (King.class) {
kingInstance = new King();
}
}
return kingInstance;
}
}Double‑Checked Locking (DCL)
public class King {
private static volatile King kingInstance;
private King() {}
public static King getKingInstance() {
if (kingInstance == null) {
synchronized (King.class) {
if (kingInstance == null) {
kingInstance = new King();
}
}
}
return kingInstance;
}
}Static Inner Class
public class King {
private King() {}
private static class KingInstance {
private static final King INSTANCE = new King();
}
public static King getInstance() { return KingInstance.INSTANCE; }
}Enum Singleton
public enum King { KINGINSTANCE; }Container‑Based Management
class InstanceManager {
private static Map
objectMap = new HashMap<>();
private InstanceManager() {}
public static void registerService(String key, Object instance) {
if (!objectMap.containsKey(key)) {
objectMap.put(key, instance);
}
}
public static Object getService(String key) { return objectMap.get(key); }
}
class Test {
public static void main(String[] args) {
InstanceManager.registerService("dog", new Dog());
Dog d1 = (Dog) InstanceManager.getService("dog");
Dog d2 = (Dog) InstanceManager.getService("dog");
System.out.println(d1);
System.out.println(d2);
}
}The table below compares the nine approaches in terms of advantages, disadvantages, and recommendation status.
Name
Advantages
Disadvantages
Recommended
Eager (static constant)
Simple, no synchronization needed
Instantiates even if never used (memory waste)
Usable
Eager (static block)
Same as above
Same as above
Usable
Lazy (thread‑unsafe)
Lazy loading
Not safe in multithreaded environments
Not recommended for multithreading
Lazy (synchronized method)
Lazy loading with thread safety
Performance overhead due to synchronization
Not recommended
Lazy (synchronized block)
Improved performance over method sync
Cannot fully guarantee singleton under heavy contention
Not recommended
Double‑checked locking
Thread‑safe, lazy, high efficiency
Requires JDK ≥ 1.5
Recommended for JDK ≥ 1.5
Static inner class
Thread‑safe, lazy, high efficiency
None known
Strongly recommended
Enum
Simple, prevents serialization attacks
Requires JDK ≥ 1.5
Recommended for JDK ≥ 1.5
Container implementation
Manages multiple singletons centrally
Extra map overhead, less common
Usable
In Android source code, Singleton is widely used, e.g., EventBus.getDefault() (double‑checked locking) and InputMethodManager.getInstance() (lazy synchronized block with a service container).
Typical real‑world scenarios include image loaders, network request managers, and utility classes. Before deciding to use a singleton, ask:
Will every component use this class in exactly the same way? Is a single instance ever sufficient? Should clients be unaware they are part of a larger application?
If the answers are all “yes”, the singleton pattern is appropriate; otherwise, avoid it.
Advantages: reduces memory and performance overhead by sharing a single instance.
Disadvantages: violates single‑responsibility principle, makes testing harder, hampers extensibility, and shared resources may become inconsistent under garbage‑collected environments.
Conclusion: The article provides a deep dive into the Singleton pattern, its nine Java implementations, their trade‑offs, Android usage, and practical guidelines for applying the pattern wisely.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.