记录一下无聊的我

1 枚举模式实现单例模式

public enum EnumSingle {
    INSTANCE;
    public  EnumSingle getInstance(){
        return INSTANCE;
    }

}

好的,来测试一下

class Client{

    public static void main(String[] args) throws Exception {
        EnumSingle s1 = EnumSingle.INSTANCE;
        EnumSingle s2 = EnumSingle.INSTANCE;
        
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

    }
}

结果没有问题,拿到的是同一个。

2 想办法破解一下

2.1序列化和反序列化破坏

class Client{

    public static void main(String[] args) throws Exception {
        writeObject2File();
        EnumSingle s1 = readObjectFromFile();
        EnumSingle s2 = readObjectFromFile();

        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());


    }

    private static  EnumSingle readObjectFromFile() throws Exception {
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        //第一个读取EnumSingle对象
        EnumSingle instance = (EnumSingle) ois.readObject();

        return instance;
    }

    public static void writeObject2File() throws Exception {
        //获取EnumSingle类的对象
        EnumSingle instance = EnumSingle.INSTANCE;
        //创建对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        //将EnumSingle对象写出到文件中
        oos.writeObject(instance);
    }
}

结果如下,很好,没有被破坏。

2.2 通过反射

class Client{

    public static void main(String[] args) throws Exception {

        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        EnumSingle s1 = constructor.newInstance();
        EnumSingle s2 = constructor.newInstance();

        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

    }
}

结果报错,,没有构造方法。

于是反编译看一下。

发现其实是有参构造。那就把获取构造器的代码改一下

Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);

再次尝试,结果如下:不能反射地创建enum对象。

这就只能点到源码看一下了。

可以看到,java它自己就不允许对枚举进行反射。所以说枚举是安全的单例。

3 一个无聊的人

其实到这里,写的虽然不咋地,但是也没有出现奇怪的操作。然后,,,,我就想,要不我把这行代码注释掉,看看是啥样吧。

于是

找到了Constructor 这个类的位置。在jre/lib/ 下rt.jar 包 java\lang\reflect\ 下面。

先把这个 jar 包备份一下。

复制一份源码,把这两行给注释掉

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

然后编译一下,把产生的class 文件替换一下。

接着,启动自己的idea,再次跑一下刚才的代码。结果如下:

好了,不一样了。

所以,完全没有啥用,还得把包给替换回来。

posted @ 2022-03-13 02:27  发呆鱼  阅读(48)  评论(0编辑  收藏  举报