饿汉式被序列化和反序列化破坏
解决办法:
单例类:实现序列化接口
//实现序列化接口,应对序列化与反序列化破坏单利模式
public class HungrySingleton implements Serializable {
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton =new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
// 解决序列化和反序列化破坏单例模式问题
// 这个方法通过反射出来,在序列化与反序列化时会通过反射形式调用此方法。
private Object readResolve(){
return hungrySingleton;
}
}
测试类:
// 序列化与反序列化的破坏
HungrySingleton instance=HungrySingleton.getInstance();//饿汉式
// 将对象序列化到文件中
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
// 将文件中的序列化对象读出来
File file=new File("singleton_file");
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
HungrySingleton newInstance=(HungrySingleton)ois.readObject();//饿汉式
System.out.println(instance);
System.out.println(newInstance);
System.out.println(instance==newInstance);
结果:
未实现接口之前输出(对象不一致):
com.tfjybj.creating.single单例.muke.HungrySingleton@7f31245a
com.tfjybj.creating.single单例.muke.HungrySingleton@6d6f6e28
false
Process finished with exit code 0
实现接口之后输出(对象一致):
com.tfjybj.creating.single单例.muke.HungrySingleton@7f31245a
com.tfjybj.creating.single单例.muke.HungrySingleton@7f31245a
true
Process finished with exit code 0
反射攻击(饿汉式和静态内部类都可以解决反射攻击,但懒汉式无法解决)
解决反射攻击,需要在单例类的构造器中加入如下代码:
// 解决反射破坏问题
if (hungrySingleton != null){
throw new RuntimeException("单例构造器禁止反射调用!");
}
栗子:
饿汉式类:
public class HungrySingleton {
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton =new HungrySingleton();
}
private HungrySingleton() {
// 解决反射破坏问题
if (hungrySingleton != null){
throw new RuntimeException("单例构造器禁止反射调用!");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
测试代码:
// 反射攻击解决方案(饿汉式和静态内部类都会遇到反射攻击)
Class objectClass = HungrySingleton.class; //饿汉式
// Class objectClass = StaticInnerClassSingleton.class; //静态内部类
// 通过反射,将HungrySingleton类构造器权限打开,获得其中对象
// 构造器类
Constructor constructor=objectClass.getDeclaredConstructor();
// 更改权限为true(可访问)
constructor.setAccessible(true);
// 普通获得对象
HungrySingleton instance=HungrySingleton.getInstance(); //饿汉式
// StaticInnerClassSingleton instance=StaticInnerClassSingleton.getInstance(); //静态内部类
// 通过反射获得对象
HungrySingleton newinstance= (HungrySingleton) constructor.newInstance(); //饿汉式
// StaticInnerClassSingleton newinstance= (StaticInnerClassSingleton) constructor.newInstance(); //静态内部类
System.out.println(instance);
System.out.println(newinstance);
System.out.println(instance==newinstance);
结果:
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:422)
at com.tfjybj.creating.single单例.muke.test.main(test.java:59)
Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用!
at com.tfjybj.creating.single单例.muke.HungrySingleton.<init>(HungrySingleton.java:21)
... 5 more
Process finished with exit code 1