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

  枚举类型天然的可序列化机制,能够强有力的保证不会出现多次实例化的情况,即使在复杂的序列化或者反射攻击的情况下,枚举类型的单例模式都没有问题。枚举类型的单例可能是实现单例模式中的最佳实践,《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");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值