AI Skill Report Card
Generated Skill
Implementing Singleton Patterns in Java
Quick Start
Enum Singleton (Recommended):
Javapublic enum ConfigSingleton { INSTANCE("default-config"); private String config; private ConfigSingleton(String config) { this.config = config; } public String getConfig() { return config; } public void setConfig(String config) { this.config = config; } } // Usage ConfigSingleton.INSTANCE.setConfig("production");
Recommendation▾
Consider adding more specific examples
Workflow
Progress:
- Identify singleton requirement (global state, expensive initialization)
- Choose implementation approach (enum preferred)
- Implement with thread safety considerations
- Test for proper singleton behavior
- Consider dependency injection alternatives
1. Enum Singleton Implementation
Javapublic enum MySingleton { INSTANCE("initial-value"); private String data; private MySingleton(String data) { this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } }
2. Class-Based Singleton (Thread-Safe)
Javapublic final class ClassSingleton { private static volatile ClassSingleton INSTANCE; private String data = "initial"; private ClassSingleton() {} public static ClassSingleton getInstance() { if (INSTANCE == null) { synchronized (ClassSingleton.class) { if (INSTANCE == null) { INSTANCE = new ClassSingleton(); } } } return INSTANCE; } public String getData() { return data; } public void setData(String data) { this.data = data; } }
3. Lazy Initialization Holder Pattern
Javapublic final class LazyInitSingleton { private String data = "initial"; private LazyInitSingleton() {} private static class Holder { static final LazyInitSingleton INSTANCE = new LazyInitSingleton(); } public static LazyInitSingleton getInstance() { return Holder.INSTANCE; } }
Recommendation▾
Include edge cases
Examples
Example 1: Database Connection Pool
Javapublic enum DatabasePool { INSTANCE; private final Connection connection; private DatabasePool() { // Expensive initialization this.connection = createConnection(); } public Connection getConnection() { return connection; } } // Usage Connection db = DatabasePool.INSTANCE.getConnection();
Example 2: Application Configuration
Javapublic enum AppConfig { INSTANCE; private Properties props = new Properties(); private AppConfig() { loadConfiguration(); } public String getProperty(String key) { return props.getProperty(key); } } // Usage String dbUrl = AppConfig.INSTANCE.getProperty("database.url");
Example 3: Testing Singleton Behavior
Java@Test public void testSingletonInstance() { MySingleton instance1 = MySingleton.INSTANCE; MySingleton instance2 = MySingleton.INSTANCE; assertSame(instance1, instance2); instance1.setData("modified"); assertEquals("modified", instance2.getData()); }
Best Practices
- Prefer Enum Singletons - Built-in serialization and thread safety
- Use volatile with double-checked locking - Prevents memory consistency errors
- Make constructor private - Prevents external instantiation
- Consider lazy initialization - Only create when needed
- Document thread safety - Be explicit about threading guarantees
- Implement proper equals/hashCode - Maintain object identity contracts
Common Pitfalls
- Non-thread-safe implementation:
Java// BAD - Race condition possible public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); // Multiple threads can reach here } return instance; }
- Reflection attacks on class-based singletons:
Java// Problem: Reflection can break singleton Constructor<ClassSingleton> constructor = ClassSingleton.class.getDeclaredConstructor(); constructor.setAccessible(true); ClassSingleton newInstance = constructor.newInstance(); // Second instance!
- Serialization breaking singleton contract:
Java// BAD - Deserialization creates new instance // Solution: Implement readResolve() private Object readResolve() { return getInstance(); }
- Using singleton as global variable:
Java// BAD - Hard to test, tight coupling public void processData() { String config = ConfigSingleton.getInstance().getValue(); } // BETTER - Dependency injection public void processData(String config) { // Explicit dependency, easier to test }
- Multiple classloader issues:
- Each classloader may create its own singleton instance
- Consider using dependency injection frameworks for complex applications
- Garbage collection edge case:
- Singleton can be GC'd if no references held
- Rare but possible in long-running applications