AI Skill Report Card
Implementing Singleton Patterns
Implementing Singleton Patterns in Java
Quick Start
Recommended: Enum Singleton (thread-safe by default)
Javapublic enum ConfigSingleton { INSTANCE; private String config = "default"; public String getConfig() { return config; } public void setConfig(String config) { this.config = config; } } // Usage ConfigSingleton.INSTANCE.setConfig("production");
Alternative: Thread-safe Class Singleton
Javapublic final class DatabaseSingleton { private static volatile DatabaseSingleton INSTANCE; private static final Object lock = new Object(); private DatabaseSingleton() {} public static DatabaseSingleton getInstance() { if (INSTANCE == null) { synchronized (lock) { if (INSTANCE == null) { INSTANCE = new DatabaseSingleton(); } } } return INSTANCE; } }
Recommendation▾
Add concrete input/output examples showing the actual behavior and state management of singleton instances across multiple calls
Workflow
Choosing Implementation:
- Use Enum Singleton - Default choice (thread-safe, serialization-safe)
- Use Class Singleton - Only when enum constraints don't fit
For Class-Based Singleton:
- Make constructor private
- Create static volatile instance field
- Implement double-checked locking in getInstance()
- Mark class as final
For Enum Singleton:
- Define single INSTANCE constant
- Add private fields and methods as needed
- No additional thread-safety code required
Recommendation▾
Include a template or framework section with ready-to-use singleton base classes or patterns for common use cases
Examples
Example 1: Configuration Manager
Javapublic enum ConfigManager { INSTANCE; private Properties config = new Properties(); public void loadConfig(String filename) { // load properties } public String getProperty(String key) { return config.getProperty(key); } } // Usage ConfigManager.INSTANCE.loadConfig("app.properties"); String dbUrl = ConfigManager.INSTANCE.getProperty("db.url");
Example 2: Logger (Class-based)
Javapublic final class Logger { private static volatile Logger INSTANCE; private static final Object lock = new Object(); private Logger() {} public static Logger getInstance() { if (INSTANCE == null) { synchronized (lock) { if (INSTANCE == null) { INSTANCE = new Logger(); } } } return INSTANCE; } public void log(String message) { System.out.println(new Date() + ": " + message); } }
Recommendation▾
Expand the edge cases section to cover classloader scenarios, serialization/deserialization behavior, and reflection attacks with concrete solutions
Best Practices
- Prefer enum singletons - Built-in thread safety and serialization
- Use volatile keyword for class-based instance fields
- Implement double-checked locking to minimize synchronization overhead
- Make constructor private and class final
- Consider dependency injection instead of global singleton access
- Pass singleton as parameter to methods rather than accessing globally
Common Pitfalls
Thread Safety Issues:
Java// WRONG: Not thread-safe public static ClassSingleton getInstance() { if (INSTANCE == null) { INSTANCE = new ClassSingleton(); // Race condition } return INSTANCE; } // WRONG: Synchronizing entire method (performance hit) public synchronized static ClassSingleton getInstance() { if (INSTANCE == null) { INSTANCE = new ClassSingleton(); } return INSTANCE; }
Design Issues:
- Don't use singletons as global variables for mutable state
- Avoid singletons that make unit testing difficult
- Don't ignore classloader issues in distributed systems
- Remember garbage collection can destroy unreferenced singletons
When NOT to use:
- When you can pass dependencies as parameters
- For configuration that needs different test/production values
- In environments with multiple classloaders
- When you need multiple instances later (violates Open/Closed principle)