Three ways I can think of are:
Serialization
If your singleton class is serializable, then you could serialize an instance of it, and deserialize it back and get a second object of that class.
You could avoid this by implementing readResolve method.
public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance(){
return INSTANCE;
}
public Object readResolve() throws ObjectStreamException {
return INSTANCE; //ensure singleton is returned upon deserialization.
}
}
Class Loading
The same class could be loaded by two different class loaders, as such, you could create two instances of your singleton class by simply invoking its getInstance
method in a class loaded by two different class loaders. This approach would work without having to resort to violating the private constructor.
ClassLoader cl1 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
ClassLoader cl2 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
Class<?> singClass1 = cl1.loadClass("hacking.Singleton");
Class<?> singClass2 = cl2.loadClass("hacking.Singleton");
//...
Method getInstance1 = singClass1.getDeclaredMethod("getInstance", ...);
Method getInstance2 = singClass2.getDeclaredMethod("getInstance", ...);
//...
Object singleton1 = getInstance1.invoke(null);
Object singleton2 = getInstance2.invoke(null);
Reflection
As you have well pointed out, via reflection you could create two instances of the class. I think the previous examples was just a variant of the same approach. But I believe you could prevent these two from happening using a SecurityManager
.
System.setSecurityManager(new SecurityManager());
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…