1.介绍
用创建单例类(Singleton)的方法可以创建多个对象(枚举方式除外)。有两种方式,可以破坏单例模式,分别是序列化和反射。
2.序列化和反序列化破坏单例模式
/**
* 懒汉式,静态内部类
* @author CharlieLiang
*
*/
//序列化要implements Serializable
public class Singleton implements Serializable{
private Singleton() {};
// 静态内部类
private static class SingletonHolder {
// 在内部类声明并初始化外部类对象
private static final Singleton INSTANCE=new Singleton();
}
// 提供公共访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 序列化破坏单例模式,可被Resolve方法解决
* @author CharlieLiang
*
*/
public class Client {
public static void main(String[] args) throws Exception {
writeObject();
Singleton s1= readObject();
Singleton s2= readObject();
System.out.println("s1的哈希值"+s1.hashCode());
System.out.println("s2的哈希值"+s2.hashCode());
System.out.println("s1的地址"+s1);
System.out.println("s2的地址"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
// 向文件读取对象
public static Singleton readObject() throws Exception {
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream("a.txt"));
Singleton s1=(Singleton) ois.readObject();
ois.close();
return s1;
}
// 向文件写对象
public static void writeObject () throws Exception {
// 1.获取Singleton对象
Singleton s1=Singleton.getInstance();
// 2.创建对象输出流对象
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("a.txt"));
// 3.写对象
oos.writeObject(s1);
// 4.释放资源
oos.close();
}
}
运行结果:
说明:上面代码运行结果是false
,表明序列化和反序列化已经破坏了单例设计模式。
3.反射破环单例模式
/**
* 懒汉式,静态内部类
* @author CharlieLiang
*
*/
public class Singleton {
// 静态内部类
private static class SingletonHolder {
// 在内部类声明并初始化外部类对象
private static final Singleton INSTANCE=new Singleton();
}
// 提供公共访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
import java.lang.reflect.Constructor;
/**
* 反射破坏单例模式(在构造方法解决)
* @author CharlieLiang
*
*/
public class Client {
public static void main(String[] args) throws Exception {
// 1.获取Singleton的字节码对象
Class clazz=Singleton.class;
// 2.获取无参构造方法
Constructor cons=clazz.getDeclaredConstructor();
// 3.取消访问检查
cons.setAccessible(true);
// 4.创建Singleton对象
Singleton s1=(Singleton) cons.newInstance();
Singleton s2=(Singleton) cons.newInstance();
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1是否等于s2"+(s1==s2));
}
}
运行结果:
说明:上面代码运行结果是false
,表明反射已经破坏了单例设计模式。
注意:枚举方式的单例不会被这两种方式破坏
4.序列化、反序列方式破坏单例模式的解决方法。
在Singleton类中添加readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
import java.io.Serializable;
/**
* 懒汉式,静态内部类
* @author CharlieLiang
*
*/
public class Singleton implements Serializable{
private Singleton() {};
// 静态内部类
private static class SingletonHolder {
// 在内部类声明并初始化外部类对象
private static final Singleton INSTANCE=new Singleton();
}
// 提供公共访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
// 当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 序列化破坏单例模式,可被Resolve方法解决
* @author CharlieLiang
*
*/
public class Client {
public static void main(String[] args) throws Exception {
writeObject();
Singleton s1= readObject();
Singleton s2= readObject();
System.out.println("s1的哈希值"+s1.hashCode());
System.out.println("s2的哈希值"+s2.hashCode());
System.out.println("s1的地址"+s1);
System.out.println("s2的地址"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
// 向文件读取对象
public static Singleton readObject() throws Exception {
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream("a.txt"));
Singleton s1=(Singleton) ois.readObject();
ois.close();
return s1;
}
// 向文件写对象
public static void writeObject () throws Exception {
// 1.获取Singleton对象
Singleton s1=Singleton.getInstance();
// 2.创建对象输出流对象
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("a.txt"));
// 3.写对象
oos.writeObject(s1);
// 4.释放资源
oos.close();
}
}
运行结果:
单例模式恢复
5.反射破获单例模式解决
/**
* 懒汉式,静态内部类
* @author CharlieLiang
*
*/
public class Singleton {
private static boolean flag=false;
private Singleton() {
synchronized (Singleton.class) {
if(flag) {
throw new RuntimeException("不能创建多个对象");
}
flag=true;
}
};
// 静态内部类
private static class SingletonHolder {
// 在内部类声明并初始化外部类对象
private static final Singleton INSTANCE=new Singleton();
}
// 提供公共访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
import java.lang.reflect.Constructor;
/**
* 反射破坏单例模式(在构造方法解决)
* @author CharlieLiang
*
*/
public class Client {
public static void main(String[] args) throws Exception {
// 1.获取Singleton的字节码对象
Class clazz=Singleton.class;
// 2.获取无参构造方法
Constructor cons=clazz.getDeclaredConstructor();
// 3.取消访问检查
cons.setAccessible(true);
// 4.创建Singleton对象
Singleton s1=(Singleton) cons.newInstance();
Singleton s2=(Singleton) cons.newInstance();
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1是否等于s2"+(s1==s2));
}
}
运行结果:
说明:
这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。