那么现在再写一种最受欢迎的单例模式,即枚举单例模式。
枚举模式的代码如下:
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 可以看到枚举没有无参构造器,而且仅有一个传两个参数的构造器,如下所示。那么我们就将这两个参数传进去,再测试。
可以看到又抛了额一个异常,但是可以很清楚的了解到这个异常是说不能反射去创建对象,我们从报错的地方进入源码查看详情。
源码清清楚楚的告诉我们如果是枚举类型,就抛出异常,可见枚举是多么的强大!