0
点赞
收藏
分享

微信扫一扫

深入了解设计模式之单例模式(三)

颜路在路上 2022-04-19 阅读 81

接上回之讲,谈到枚举单例模式没有定义readReslove()方法,但是在反序列化的时候返回的仍是同一个对象,下面我们来揭开它的什么面纱:

下载一个非常好用的反编译工具Jad(下载地址:https://varaneckas.com/jad/),解压后配置环境变量,就可以使用命令行调用了。

After thirty minutes.........

我电脑一直使用不了Jad反编译工具,又换了其他jd-gui也没给到我想要的效果,直接用javap -c进行反编译吧,代码如下:

原来,枚举式单例模式在静态代码块中就给INSTANCE进行了赋值,是饿汉式单例模式的实现,那为什么序列化破坏不了呢,我们回到JDK源码,还是ObjectInputStream的readObject0()方法:

private Object readObject0(Class<?> type, boolean unshared) throws IOException {

...
 case TC_ENUM:
           if (type == String.class) {
                  throw new ClassCastException("Cannot cast an enum to java.lang.String");
                    }
           return checkResolve(readEnum(unshared));
...

 我们看到,又调用了readEnum()方法,我们接下来来看readEnum()方法的实现:

private Enum<?> readEnum(boolean unshared) throws IOException {
        if (bin.readByte() != TC_ENUM) {
            throw new InternalError();
        }

        ObjectStreamClass desc = readClassDesc(false);
        if (!desc.isEnum()) {
            throw new InvalidClassException("non-enum class: " + desc);
        }

        int enumHandle = handles.assign(unshared ? unsharedMarker : null);
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(enumHandle, resolveEx);
        }

        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;
    }

我们发现,枚举类型其实通过类名和类对象找到一个唯一的枚举对象。因此,枚举对象不可能被类加载器加载多次。那么反射能否破坏呢?我们来看一段代码:

class Test {
    public static void main(String[] args) {
        Class<?> clazz = Solution.class;
        try {
            Object o = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下图所示:

 居然没找到无参的构造方法,接下来我们去看Enum类的源码,因为我们在javap反编译的时候看到此时的类继承了java.lang.Enum类:

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

 我们发现父类只有一个有参构造方法,那我们利用反射来调用有参构造方法,代码如下:

class Test {
    public static void main(String[] args) {
        Class<?> clazz = Solution.class;
        try {
            Constructor<?> c = clazz.getDeclaredConstructor(String.class, int.class);
            c.setAccessible(true);
            Object o = c.newInstance("zyy",21);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果如下:

这时候错误已经非常明显了,不用利用反射来创建枚举类型,还是习惯的看看JDK源码,进入Constructor的newInstance()方法 :

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

从上述代码可以看出,方法里做了强制性的判断,如果是ENUM类型,直接抛出异常。当然,这种枚举单例模式也是Effective Java书中推荐的实现写法。

单例模式小结

最后我们来做一下总结,单例模式可以保证内存里只有一个实例,减少内存开销,单例模式看起来非常简单,实现起来也非常简单,但是在面试中是个高频考点,希望小伙伴们通过学习,遇到类似问题直接上源码分析,彰显技术深度,提高核心竞争力。

举报

相关推荐

0 条评论