设为首页 加入收藏

TOP

设计模式 - 单例模式之多线程调试与破坏单例(四)
2019-10-10 11:17:42 】 浏览:493
Tags:设计模式 单例 模式 线程 调试 破坏
ends Enum { public static EnumSingleton[] values() { return (EnumSingleton[])$VALUES.clone(); } public static EnumSingleton valueOf(String name) { return (EnumSingleton)Enum.valueOf(com/eamon/javadesignpatterns/singleton/enums/EnumSingleton, name); } private EnumSingleton(String s, int i) { super(s, i); instance = new EnumResource(); } public Object getInstance() { return instance; } public static final EnumSingleton INSTANCE; private Object instance; private static final EnumSingleton $VALUES[]; static { INSTANCE = new EnumSingleton("INSTANCE", 0); $VALUES = (new EnumSingleton[] { INSTANCE }); } }

请注意这段代码:

static
{
    INSTANCE = new EnumSingleton("INSTANCE", 0);
    $VALUES = (new EnumSingleton[] {
        INSTANCE
    });
}

原来枚举类单例在静态代码块中就给INSTANCE 赋了值,是饿汉式单例的实现方式。那么同样的,我们能否通过反射和序列化方式进行破坏呢?

先分析通过序列化方式:

我们还是回到JDK源码:在 ObjectInputStreamreadObject0()方法中有如下代码:

 private Object readObject0(boolean unshared) throws IOException {
    ...
        case 126:
            var4 = this.checkResolve(this.readEnum(unshared));
    ...

    return var4;
}

我们看到 readObject0()中调用了readEnum()方法,跟进该方法:

private Enum<?> readEnum(boolean unshared) throws IOException {
    if (this.bin.readByte() != 126) {
        throw new InternalError();
    } else {
        ObjectStreamClass desc = this.readClassDesc(false);
        if (!desc.isEnum()) {
            throw new InvalidClassException("non-enum class: " + desc);
        } else {
            int enumHandle = this.handles.assign(unshared ? unsharedMarker : null);
            ClassNotFoundException resolveEx = desc.getResolveException();
            if (resolveEx != null) {
                this.handles.markException(enumHandle, resolveEx);
            }

            String name = this.readString(false);
            Enum<?> result = null;
            Class<?> cl = desc.forClass();
            if (cl != null) {
                try {
                    Enum<?> en = Enum.valueOf(cl, name);
                    result = en;
                } catch (IllegalArgumentException var9) {
                    throw (IOException)(new InvalidObjectException("enum constant " + name + " does not exist in " + cl)).initCause(var9);
                }

                if (!unshared) {
                    this.handles.setObject(enumHandle, result);
                }
            }

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

我们发现枚举类型其实通过类名和 Class 对象类找到一个唯一的枚举对象。因此,枚举对象不可能被类加载器加载多次。

那么是否可以通过反射进行破坏呢?我们先来执行以下反射破坏枚举类的测试代码:

@Test
public void testEnum(){
    try {
        // 很无聊的情况下,进行破坏
        Class<EnumSingleton> clazz = EnumSingleton.class;
        // 通过反射拿到私有的构造方法
        Constructor<EnumSingleton> c = clazz.getDeclaredConstructor(null);
        // 设置访问属性,强制访问
        c.setAccessible(true);

        // 暴力初始化两次,这就相当于调用了两次构造方法
        EnumSingleton o1 = c.newInstance();
        EnumSingleton o2 = c.newInstance();
        // 只要 o1和o2 地址不相等,就可以说明这是两个不同的对象,也就是违背了单例模式的初衷
        System.out.println(o1 == o2);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

执行结果:

报的是 java.lang.NoSuchMethodException 异常,意思是没找到无参的构造方法。

那么我们来看一下 java.lang.Enum 的源码,我们发现它只有一个protected的构造方法:

protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

那我们来做一个这样的测试:

@Test
public void testEnum1() {
    try {
        Class clazz = EnumSingleton.class;
        Constructor c = clazz.getDeclaredConstructor(String.class, int.class);
        c.setAccessible(true);
        EnumSingleton enumSingleton = (EnumSingleton) c.newInstance("Eamon", 666);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

发现控制台输出如下错误:

意思就是不能用反射来创建枚举类型。至于为什么,我们还是来

首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇设计模式-行为型-访问者模式 下一篇通俗易懂设计模式解析——迭代器..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目