首先提供一个双重加锁的单例模式
public class CheckSingleton{
private CheckSingleton(){};
private volatile static CheckSingleton checksingleton;
public static CheckSingleton getInstance(){
if(null==checksingleton){
synchronized(CheckSingleton.class){
if(null==checksingleton){
checksingleton = new CheckSingleton();
}
}
}
return checksingleton;
}
}
一、【反射破坏单例】
import java.lang.reflect.Constructor;
public class SingletonTest {
public static void main(String[] args) {
CheckSingleton singleton = CheckSingleton.getSingleton();
try {
Class<CheckSingleton> singleClass = (Class<Singleton>)Class.forName("com.dev.interview.CheckSingleton");
Constructor<CheckSingleton> constructor = singleClass.getDeclaredConstructor(null);
constructor.setAccessible(true);
CheckSingleton singletonByReflect = constructor.newInstance();
System.out.println("singleton : " + singleton);
System.out.println("singletonByReflect : " + singletonByReflect);
System.out.println("singleton == singletonByReflect : " + (singleton == singletonByReflect));
} catch (Exception e) {
e.printStackTrace();
}
}
}
发现发射生成的一个新的对象 ,此时单例模式被破坏
防止方案(设置为如果通过反射创建则抛出异常):
private Singleton() {
if (null!= CheckSingleton) {
throw new RuntimeException("Singleton constructor is called... ");
}
}
二、反序列化生成新的实例
public class SingletonTest {
public static void main(String[] args) {
CheckSingleton singleton = CheckSingleton.getSingleton();
//Write Obj to file
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
oos.writeObject(singleton);
//Read Obj from file
File file = new File("tempFile");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Singleton singletonBySerialize = (CheckSingleton)ois.readObject();
//判断是否是同一个对象
System.out.println("singleton : " + singleton);
System.out.println("singletonBySerialize : " + singletonBySerialize);
System.out.println("singleton == singletonBySerialize : " + (singleton == singletonBySerialize));
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过先序列化再反序列化的方式,可获取到一个新的单例对象,这就破坏了单例。
避免方案(在单例中加入readResolve方法,因为在反序列化执行过程中会执行到ObjectInputStream#readOrdinaryObject方法,这个方法会判断对象是否包含readResolve方法,如果包含的话会直接调用这个方法获得对象实例。)
private Object readResolve() {
return getSingleton();
}
如果没有该方法,会通过反序列化中特殊的反射方式得到对象,和上边第一种破坏方式中的反射不一样。
三、建议使用枚举做单例模式,天生安全。
public enum Singleton{
INSTANCE;
public void say(){
System.out.print("hello ,enum")
}
}