如何使类A及其任意子类通过new仅能创建单实例?(总监四轮面试题)
Great question! Let's tackle this problem step by step. The goal is to ensure that both the base class A and any of its subclasses can only be instantiated once via the new keyword—with an exception thrown on subsequent attempts—and all enforcement logic lives inside the class hierarchy, not the main method.
Solution Code
First, here's the implementation for the base class A:
import java.util.HashSet; import java.util.Set; public class A { // Static set to track which classes have already been instantiated private static final Set<Class<?>> INSTANTIATED_CLASSES = new HashSet<>(); public A() { Class<?> currentClass = this.getClass(); // Synchronize to prevent race conditions in multi-threaded environments synchronized (INSTANTIATED_CLASSES) { if (INSTANTIATED_CLASSES.contains(currentClass)) { throw new IllegalStateException( "Cannot create more than one instance of " + currentClass.getName() ); } INSTANTIATED_CLASSES.add(currentClass); } } }
A subclass (like E) requires no extra code—its constructor will automatically inherit the parent class's instantiation check:
public class E extends A { // No additional logic needed; parent constructor handles the singleton check }
And here's the test main method matching your example:
public class Main { public static void main(String[] args) { try { A obj1 = new A(); // Creates instance successfully System.out.println("Created instance of A"); A obj2 = new A(); // Throws exception } catch (IllegalStateException e) { System.out.println(e.getMessage()); } try { E obj3 = new E(); // Creates instance successfully System.out.println("Created instance of E"); E obj4 = new E(); // Throws exception } catch (IllegalStateException e) { System.out.println(e.getMessage()); } } }
Key Explanations
- Static Class Tracking: The
INSTANTIATED_CLASSESset stores theClassobject of each type that's been instantiated. Since each class has a uniqueClassinstance, this ensures we track every subclass and the base class individually. - Constructor Enforcement: The check happens directly in the base class constructor. Every subclass (whether using the default constructor or a custom one) must call the parent constructor (either explicitly with
super()or implicitly), so the validation logic applies to all subclasses automatically. - Thread Safety: The
synchronizedblock around the set operations prevents race conditions where multiple threads might try to instantiate the same class at the same time, bypassing the check. - Exception Type:
IllegalStateExceptionis the right choice here because attempting to create a second instance violates the intended state of the class.
Edge Cases to Consider
- Custom Subclass Constructors: Even if a subclass defines its own constructor (with parameters), as long as it calls
super()(which it must, either explicitly or implicitly), the singleton check will still work. For example:public class B extends A { public B(String value) { super(); // Triggers the instantiation check // Subclass-specific logic here } } - Reflection Protection: This implementation even blocks reflection-based duplicate instantiation. If someone uses reflection to call the constructor a second time, the check in the constructor will still trigger and throw an exception.
内容的提问来源于stack exchange,提问作者Randhir
相关产品推荐
相关产品推荐

