那么现在再写一种最受欢迎的单例模式,即枚举单例模式。
枚举模式的代码如下:
date是为了测试方便。
public enum EnumInstance {
INSTANCE;
private Object date;
public Object getDate() {
return date;
}
public void setDate(Object date) {
this.date = date;
}
public static EnumInstance getInstance(){
return INSTANCE;
}
}
1、那么我们可以写一个序列化的栗子进行测试。
public class SerivalTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
EnumInstance instance = EnumInstance.getInstance();
instance.setDate(new Object());
//放文件
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("single_file"));
outputStream.writeObject(instance);
//取文件
File file = new File("single_file");
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
EnumInstance hungrySingleton = (EnumInstance) inputStream.readObject();
System.out.println(instance);
System.out.println(hungrySingleton);
System.out.println(instance==hungrySingleton);
}
}
结果为:

怎么样?枚举就是这么强大,那么序列化和反序列化对枚举是怎么处理的呢?首先通过inputStream.readObject()进入,找到readEnum()方法。
如下:
是通过类型和name进行获得枚举常量,因为枚举中的name是唯一的,并且对应一个枚举常量,所以2012行拿到的肯定是唯一的常量对象,
这样呢就没有创建新的对象。维持了这个对象的单例属性。枚举中这个处理方法还是很简单的,而且很容易理解,所以枚举类对于序列化
这个破坏是不受影响的。

2、写一个反射攻击的例子。
public class Testreflection {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class object = EnumInstance.class;
Constructor constructor = object.getDeclaredConstructor();
constructor.setAccessible(true);
EnumInstance instance = EnumInstance.getInstance();
EnumInstance newInstance = (EnumInstance) constructor.newInstance();
System.out.println(instance.getDate());
System.out.println(newInstance.getDate());
System.out.println(instance.getDate() == newInstance.getDate());
}
}
看结果:

表示没有获得无参构造器,那么我们打开源码看一哈。java.lang.enum 可以看到枚举没有无参构造器,而且仅有一个传两个参数的构造器,如下所示。那么我们就将这两个参数传进去,再测试。


可以看到又抛了额一个异常,但是可以很清楚的了解到这个异常是说不能反射去创建对象,我们从报错的地方进入源码查看详情。

源码清清楚楚的告诉我们如果是枚举类型,就抛出异常,可见枚举是多么的强大!