反射破坏单例
这里是一个 饿汉单例模式
package com.singleton.pattern.singleton.demo;
import java.io.Serializable;
/**
* @Classname HungrySingleton
* @Description 饿汉模式
* @Date 2019/11/20 0020 15:23
* @Created by 埔枘
*/
public class HungrySingleton implements Serializable {
private static HungrySingleton hungrySingleton = new HungrySingleton();
/**
* 私有化 空构造
*/
private HungrySingleton(){
}
public static HungrySingleton getSingleton(){
return hungrySingleton;
}
}
下简单演示 通过 反射破坏单例
单例的首要原则就是 构造方法私有,但是 通过反射 构造对象后,构造方法私有 变为无效,从而导致 单例被破坏
package com.singleton.pattern.singleton.sabotage;
import com.singleton.pattern.singleton.demo.HungrySingleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 破坏单例
*/
public class Sabottage {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
/**
* 通过反射 破坏单例
*
* 反射会强制执行 私有的构造方法 造成单例失效
* 懒加载,饿加载 都会存在此问题
*
* 防止方案:
* 从构造方法中 判断引用是否为空
*/
Class cls = HungrySingleton.class;
Constructor declaredConstructor = cls.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//强吻
Object o1 = declaredConstructor.newInstance();
Object o2 = declaredConstructor.newInstance();
System.out.println(o1);
System.out.println(o2);
}
}

那么我们这么解决这个问题呢?
既然我们不能阻止 别人反射本类,那么我在你必经之路(构造方法 中判断一下对象是否被初始化了不就好了吗?)
在 构造方法中判断 实例是否已经初始化过了
package com.singleton.pattern.singleton.demo;
import java.io.Serializable;
/**
* @Classname HungrySingleton
* @Description 饿汉模式
* @Date 2019/11/20 0020 15:23
* @Created by 埔枘
*/
public class HungrySingleton implements Serializable {
private static HungrySingleton hungrySingleton = new HungrySingleton();
/**
* 私有化 空构造
*/
private HungrySingleton(){
if(hungrySingleton!=null){
throw new RuntimeException("不能二次实例化对象");
}
}
public static HungrySingleton getSingleton(){
return hungrySingleton;
}
}
可以看到 程序直接抛出了异常,阻止了 程序破坏单例

序列化破坏单例
序列化破坏单例
通过序列化把 实例 保存到文件系统中,再读到程序中,形成了 单例的破坏。

package com.singleton.pattern.singleton.sabotage;
import com.singleton.pattern.singleton.demo.HungrySingleton;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
/**
* 破坏单例
*/
public class Sabottage {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
HungrySingleton s1 = null;
HungrySingleton s2 = HungrySingleton.getSingleton();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("HungrySingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("HungrySingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (HungrySingleton)ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
为什么序列化对象会导致 单例被破坏呢?
查看 ObjectInputStream 的源码发现 如下代码


继续看,这里主要判断了this.cons !=null

发现cons 指的是构造方法,也就是说只要你有构造方法 那么我就 new 一个新的实例返回出去,形成单例的破坏

那么怎么防止 序列化带来的 单例破坏问题呢?
我们继续看源码,
看到 这行代码
执行传入对象的 readResolve 方法,但是我们的 单例里没有这个方法呀
Object rep = desc.invokeReadResolve(obj);

通过全局查找

package com.singleton.pattern.singleton.demo;
import java.io.Serializable;
/**
* @Classname HungrySingleton
* @Description 饿汉模式
* @Date 2019/11/20 0020 15:23
* @Created by 埔枘
*/
public class HungrySingleton implements Serializable {
private static HungrySingleton hungrySingleton = new HungrySingleton();
/**
* 私有化 空构造
*/
private HungrySingleton(){
if(hungrySingleton!=null){
throw new RuntimeException("不能二次实例化对象");
}
}
public static HungrySingleton getSingleton(){
return hungrySingleton;
}
public Object readResolve(){
return HungrySingleton.hungrySingleton;
}
}
再次执行 发现 成功 防止了 序列化问题 导致的 单例破坏。

总结
反射破坏单例
懒汉模式,饿汉模式,容器式单例都会受影响,都有构造方法 尽管设置了 私有,但反射技术 可以实现 “强吻”,枚举单例免疫(枚举没有构造)
解决方案:在构造方法中判断 引用对象是否已经初始化过了。
序列化破坏单例
懒汉模式,饿汉模式,容器式单例都会受影响,都有构造方法 尽管设置了 私有,但反射技术 可以实现 “强吻”,枚举单例免疫(枚举没有构造)
解决方案: 加上 readResolve() 方法