这里单例模式就不多说了
23种设计模式之单例模式
1. 序列化和反序列化
这里用饿汉式来做例子
LazySingleton
import java.io.Serializable;
public class LazySingleton implements Serializable {
private static LazySingleton lazySingleton = null;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
TestSerializer
import java.io.*;
public class TestSerializer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 懒汉式
LazySingleton instance = LazySingleton.getInstance();
// 通过序列化和反序列化的方式,创建对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton"));
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton")));
LazySingleton objInstance = (LazySingleton)ois.readObject();
System.out.println(instance);
System.out.println(objInstance);
}
}
输出结果:
yxz.singleton.LazySingleton@3764951d
yxz.singleton.LazySingleton@312b1dae
可以看到这俩对象不一样。
但是使用枚举输出结果是一样的。
如果不让其破坏呢???
我们在单例中添加一个方法。
import java.io.Serializable;
public class LazySingleton implements Serializable {
private static LazySingleton lazySingleton = null;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
private Object readResolve() {
return lazySingleton;
}
}
输出结果:
yxz.singleton.LazySingleton@3764951d
yxz.singleton.LazySingleton@3764951d
这样输出结果是一样的了。因为反序列化创建对象时,是通过反射创建的,反射会调用我们自己的readResolve
方法,如果重写,会调用这个,否则会破坏单例模式。
2. 反射
通过字节码对象创建构造器对象,通过构造器对象,初始化单例对象,由于单例对象的构造方法是私有化的,调用构造器中的方法,赋予权限,创建单例对象。
TestReflect
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestReflect {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class clz = LazySingleton.class;
// 通过字节码对象创建构造器对象
Constructor constructor = clz.getDeclaredConstructor();
// 赋予权限
constructor.setAccessible(true);
// 初始化单例对象
LazySingleton clzInstance = (LazySingleton)constructor.newInstance();
System.out.println(clzInstance);
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}
}
输出结果:
yxz.singleton.LazySingleton@1b6d3586
yxz.singleton.LazySingleton@4554617c
坏了,又是一样的,这可怎么办!!!
我们如何阻止呢?
我们对代码的构造模式进行修改
import java.io.Serializable;
public class LazySingleton implements Serializable {
private static LazySingleton lazySingleton = null;
private LazySingleton() {
if(lazySingleton != null){
throw new RuntimeException("不允许创建多个实例");
}
}
public static synchronized LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
private Object readResolve() {
return lazySingleton;
}
}
这样我们再运行,输出结果是:
yxz.singleton.LazySingleton@1b6d3586
yxz.singleton.LazySingleton@4554617c
恭喜你,仍然能出现两个!!!但是,我们先创建一个对象,再使用反射呢?
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestReflect {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
Class clz = LazySingleton.class;
Constructor constructor = clz.getDeclaredConstructor();
constructor.setAccessible(true);
LazySingleton clzInstance = (LazySingleton)constructor.newInstance();
System.out.println(clzInstance);
}
}
运行结果:
yxz.singleton.LazySingleton@1b6d3586
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at yxz.singleton.broker.TestReflect.main(TestReflect.java:16)
Caused by: java.lang.RuntimeException: 不允许创建多个实例
at yxz.singleton.LazySingleton.<init>(LazySingleton.java:10)
... 5 more
这才没有创建多个。
当然,我建议是使用枚举来组织它,不过,我在使用的时候,还是没怎怎么故意使用反射或者序列化这些来破坏单例模式。