先看一个单例:
public class Singleton{ private final static Singleton INSTANCE = new Singleton(); private Singleton(){}; public static Singleton getInstance(){return INSTANCE;} }
我们用序列化来打破单例
public class Singleton implements Serializable{ private final static Singleton INSTANCE = new Singleton(); private Singleton(){}; public static Singleton getInstance(){return INSTANCE;} public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Singleton s1 = Singleton.getInstance(); File objectF = new File("/object"); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(objectF)); out.writeObject(s1); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream(objectF)); Singleton s2 = (Singleton) in.readObject(); in.close(); System.out.println("是单例么?" + (s1 == s2)); } }
将会打印:
是单例么?false。
可见我们可以这样破坏其单例属性。要保持应该怎么办呢?需要增加readResolve方法,Java反序列化的时候会用这个方法的返回值直接代替序列化得到的对象
public class Singleton implements Serializable{ private final static Singleton INSTANCE = new Singleton(); private Singleton(){}; public static Singleton getInstance(){return INSTANCE;} private Object readResolve() { return INSTANCE; } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Singleton s1 = Singleton.getInstance(); File objectF = new File("/object"); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(objectF)); out.writeObject(s1); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream(objectF)); Singleton s2 = (Singleton) in.readObject(); in.close(); System.out.println("是单例么?" + (s1 == s2)); } }打印:
是单例么?true
我们再通过反射来打破其的单例性:
public class Singleton{ private final static Singleton INSTANCE = new Singleton(); private Singleton(){}; public static Singleton getInstance(){return INSTANCE;} public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Singleton s1 = Singleton.getInstance(); Constructor<Singleton> c = Singleton.class.getDeclaredConstructor(); c.setAccessible(true); Singleton s2 = c.newInstance(); System.out.println("是单例么?" + (s1 == s2)); } }
将会打印:
是单例么?false。
说明使用反射调用私有构造器也是可以破坏单例的,解决的办法是如下:public class Singleton{ private final static Singleton INSTANCE = new Singleton(); private Singleton(){ if(++COUNT > 1){ throw new RuntimeException("can not be construt more than once"); } }; private static int COUNT = 0; public static Singleton getInstance(){ return INSTANCE; } }
这样当使用反射调用的时候,就会抛出异常。
再用clone来破坏单例性
public class Singleton implements Cloneable{ private final static Singleton INSTANCE = new Singleton(); public static Singleton getInstance(){ return INSTANCE; } public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = (Singleton) s1.clone(); System.out.println("是单例么?" + (s1 == s2)); } }
这样也会发现不是单例了,办法是重新clone方法。
public class Singleton implements Cloneable{ private final static Singleton INSTANCE = new Singleton(); public static Singleton getInstance(){ return INSTANCE; } @Override protected Object clone() throws CloneNotSupportedException { return INSTANCE; } public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = (Singleton) s1.clone(); System.out.println("是单例么?" + (s1 == s2)); } }
如果想要比较简便的避免上诉的问题,最好的方式是使用枚举:
public enum SingleEnum { INSTANCE; public static SingleEnum getInstance(){ return INSTANCE; } }
其通过反射会抛出如下异常:
java.lang.NoSuchMethodException: com.price.effective.create.SingleEnum.<init>()
通过反序列化其也会返回INSTANCE对象。
其没有clone方法
综上,Enum可以作为想用单例时的第一选择。