Sitemap
Tadeo Aragón

Tadeo Aragón

Full Stack dev 😀

Press enter or click to view image in full size

Singleton Pattern and Enum Singleton

5 min readJun 19, 2025

--

The Singleton pattern ensures that a class has only one instance and provides a global access point to that instance. It is commonly used for shared resources like database connections, thread pools, or configuration settings, where having multiple instances could cause security risks, performance issues, or inconsistent state.

Football Ball Analogy

In a soccer match, there’s only one ball in play at any given time (even if there are spare balls, logically only one is “active”).

Why Singleton?

  • There should be only one instance of the soccer ball during the game.
  • All players (other objects in the system) must interact with the same ball.
  • If someone tries to create a “new ball,” the system should return the existing one

Java Implementation

public class SoccerBall {
// The single instance of the ball (static + private)
private static SoccerBall instance;
private String status; // e.g., "in play", "out of bounds"

// Private constructor to prevent direct instantiation
private SoccerBall() {
this.status = "in play"; // Default state
}

// Global access point to the ball
public static SoccerBall getInstance() {
if (instance == null) {

instance = new SoccerBall(); // Creates the ball if it
//doesn't exist
}
return instance;
}

// Methods to interact with the ball
public void kick() {
System.out.println("Ball was kicked!");
}

public void setStatus(String status) {
this.status = status;
}

public String getStatus() {
return status;
}
}

How It Works in the “Soccer Game” System

//All players share the same ball
SoccerBall ball1 = SoccerBall.getInstance();
SoccerBall ball2 = SoccerBall.getInstance();

System.out.println(ball1 == ball2); // true (same object)

//No duplicate balls can be created
//new SoccerBall() is impossible (constructor is private).

//Controlled state management
ball1.kick(); // "Ball was kicked!"
ball1.setStatus("out of bounds");
System.out.println(ball2.getStatus()); // "out of bounds" (shared state)

Key Takeaways

  • Single source of truth: The ball’s state (position, status) is consistent across the entire system.
  • Global access: Any player/class can get the ball via SoccerBall.getInstance().
  • No duplicates: Ensures no "illegal second ball" is accidentally created.

When NOT to Use Singleton

  • If multiple balls could be in play (e.g., training session with many balls).
  • If the ball’s state doesn’t need to be shared.

The Singleton pattern is designed for classes that should only have one instance. If your application requires multiple instances of a class, then Singleton is not the appropriate pattern. To determine when to use Singleton, consider the soccer ball analogy: in a real match there can only be one official ball in play (Singleton), while during practice sessions multiple balls may be used simultaneously (regular class). When in doubt, ask yourself: ‘Does this scenario truly require a single, shared instance?

Common Problems and Pitfalls in Singleton Implementation and solutions

While the Singleton pattern appears simple to implement, developers often overlook critical aspects that can lead to subtle bugs. Below are key challenges with their recommended solutions:

  1. Thread Safety Issues
  • Problem: Multiple threads may create separate instances
  • Solution: Use double-checked locking with volatile or static initialization.

2. Reflection Vulnerabilities

    Get Tadeo Aragón’s stories in your inbox

    Join Medium for free to get updates from this writer.

  • Problem: Attackers can bypass private constructors
  • Solution: Throw exception if constructor is called when instance exists

3. Serialization Problems

  • Problem: Deserialization creates duplicate instances
  • Solution: Implement readResolve() method

4. Cloning Risks

  • Problem: clone() method can create copies
  • Solution: Override clone() to throw CloneNotSupportedException

5. Testing Difficulties

  • Problem: Global state makes unit testing challenging
  • Solution: Consider dependency injection for testable design

6. Memory Management

  • Problem: Static instance may never be garbage collected
  • Solution: Use lazy initialization when appropriate
import java.io.Serializable;

/*
* Thread-safe Singleton implementation of a soccer ball
* with protection against reflection, serialization, and cloning attacks.
*/
public final class SoccerBall implements Serializable {
// volatile ensures visibility across threads
private static volatile SoccerBall instance;
private String status;

// Private constructor with reflection protection
private SoccerBall() {
if (instance != null) {
throw new IllegalStateException("Ball already exists! Use getInstance() instead.");
}
this.status = "in play"; // Default state
}

/**
* Global access point using double-checked locking pattern
* @return The single instance of SoccerBall
*/
public static SoccerBall getInstance() {
SoccerBall result = instance; // Local variable improves performance
if (result == null) {
synchronized (SoccerBall.class) {
result = instance;
if (result == null) {
instance = result = new SoccerBall();
}
}
}
return result;
}

// Ball behavior methods
public void kick() {
System.out.println("Ball was kicked!");
}

public void setStatus(String status) {
this.status = status;
}

public String getStatus() {
return status;
}

// Prevent serialization from creating new instances
protected Object readResolve() {
return getInstance();
}

// Prevent cloning
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton cannot be cloned");
}
}

Enum Singleton

The best way to implement this in Java? Using an enum, as recommended by Joshua Bloch (Java expert and author of Effective Java). Joshua Bloch introduced this approach in Effective Java (Item 3: “Enforce the singleton property with a private constructor or an enum type”) because:

  • Guarantees a single instance (no duplicates, even with reflection or serialization).
  • Thread-safe by design (no race conditions in multi-threaded apps).
  • Simple and concise (no need for synchronized or double-checked locking).

“This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.”
— Joshua Bloch, Effective Java

Example: The Singleton Soccer Ball (enum Implementation)

public enum SoccerBall {
MATCH_BALL; // The one and only instance!

// Ball properties
private String brand = "Nike";
private int airPressure = 9; // Optimal PSI

// Private constructor (runs only once)
private SoccerBall() {
System.out.println("The official match ball is ready!");
}

// Methods to interact with the ball
public void kick() {
System.out.println("The " + brand + " ball was kicked at " + airPressure + " PSI!");
}

public void adjustPressure(int newPressure) {
this.airPressure = newPressure;
System.out.println("Ball pressure updated to " + airPressure + " PSI.");
}
}

Using the Ball in a Match (Client Code)

public class SoccerGame {
public static void main(String[] args) {
// All players use the same ball
SoccerBall gameBall = SoccerBall.MATCH_BALL;

gameBall.kick(); // "The Nike ball was kicked at 9 PSI!"

// Referee adjusts pressure
gameBall.adjustPressure(10); // " Ball pressure updated to 10 PSI."

// Messi and Ronaldo share the same ball
SoccerBall messisBall = SoccerBall.MATCH_BALL;
SoccerBall ronaldosBall = SoccerBall.MATCH_BALL;

System.out.println("Do Messi and Ronaldo use the same ball? " + (messisBall == ronaldosBall)); // true
}
}

/*
// ERROR: Not allowed!
SoccerBall fakeBall = new SoccerBall(); // Compilation error
*/

I hope you’ve found this guide helpful for properly implementing the Singleton pattern. You now understand not just the basic structure, but also the critical challenges and how to solve them effectively. Remember: a well-implemented Singleton should be thread-safe, resistant to reflection and serialization, and easy to maintain.

If you enjoyed this practical breakdown of the Singleton pattern using Java, you’ll find even more hands-on insights in:
📘 Java Design Patterns: A Hands-On Experience with Real-World Examples by Vaskaran Sarcar

🔗 Bloch’s Effective Java (Item 3) to master both classical and modern Java-specific pattern implementations.

--

--