枚举实现单例模式可避免序列化和反射攻击

  枚举类型天然的可序列化机制,能够强有力的保证不会出现多次实例化的情况,即使在复杂的序列化或者反射攻击的情况下,枚举类型的单例模式都没有问题。枚举类型的单例可能是实现单例模式中的最佳实践,《Effcetive Java》这本书也是强力推荐这种写法。

1、枚举实现单例模式

public enum EnumInstance {
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumInstance getInstance(){
        return INSTANCE;
    }
}

2、序列化测试

    EnumInstance enumInstance = EnumInstance.getInstance();
    enumInstance.setData(new Object());
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
    oos.writeObject(enumInstance);

    File file = new File("singleton_file");
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
    EnumInstance newEnumSingleton = (EnumInstance) ois.readObject();

    System.out.println(enumInstance.getData());
    System.out.println(newEnumSingleton.getData());
    System.out.println(enumInstance.getData()==newEnumSingleton.getData());

输出结果:
  java.lang.Object@2f4d3709
  java.lang.Object@2f4d3709
  true

  在ObjectInputStream类中有一个readEnum方法,通过readString方法获取到枚举对象的名称,再通过类型和名称来获取常量,因为枚举中的名称是唯一的,对应一个枚举常量,所以获取的常量一定是唯一的常量对象,这样就没有创建新的对象,维持了这个类的单例属性。

/**
     * Reads in and returns enum constant, or null if enum type is
     * unresolvable.  Sets passHandle to enum constant's assigned handle.
     */
    private Enum<?> readEnum(boolean unshared) throws IOException {
        // 校验部分代码已除去
        
        String name = readString(false);
        Enum<?> result = null;
        Class<?> cl = desc.forClass();
        if (cl != null) {
            try {
                @SuppressWarnings("unchecked")
                Enum<?> en = Enum.valueOf((Class)cl, name);
                result = en;
            } catch (IllegalArgumentException ex) {
                throw (IOException) new InvalidObjectException(
                    "enum constant " + name + " does not exist in " +
                    cl).initCause(ex);
            }
            if (!unshared) {
                handles.setObject(enumHandle, result);
            }
        }

        handles.finish(enumHandle);
        passHandle = enumHandle;
        return result;
    }

3、反射测试

    Class objectClass = EnumInstance.class;
    Constructor declaredConstructor = objectClass.getDeclaredConstructor(String.class, int.class);
    // 修改私有构造器的访问权限
    declaredConstructor.setAccessible(true);
    EnumInstance enumInstance = (EnumInstance) declaredConstructor.newInstance("singleton", 1);

  执行上述代码会抛出如下异常:java.lang.IllegalArgumentException: Cannot reflectively create enum objects,表示不同通过反射来创建枚举类的对象,在Constructor类中的newInstance()方法中有下列代码,当检测出类型为枚举类型,即抛出异常。

if ((clazz.getModifiers() & Modifier.ENUM) != 0){
     throw new IllegalArgumentException("Cannot reflectively create enum objects");
}
发布了11 篇原创文章 · 获赞 1 · 访问量 260

猜你喜欢

转载自blog.csdn.net/Introncheng/article/details/103132214