设计模式课程 设计模式精讲 8-8 单例设计模式-Enum枚举单例、原理源码解析以及反编译实战...

1    课堂解析

2    代码演练

2.1  枚举类单例解决序列化破坏demo

2.2  枚举类单例解决序列化破坏原理

2.3  枚举类单例解决反射攻击demo

2.4  枚举类单例解决反射攻击原理

3    jad的使用

3.1  网址:

 

1    课堂解析
2    代码演练
2.1  枚举类单例解决序列化破坏demo

枚举类:

package com.geely.design.pattern.creational.singleton;

/**
 * 这个类是enum类型
 */
public enum  EnumInstance {
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
/**
     * Java类中的静态变量在程序运行期间,其内存空间对所有该类的对象实例而言是共享的,有些时候可以认为是全局变量。
     * 因此在某些时候为了节省系统内存开销、共享资源,可以将类中的一些变量声明为静态变量!
     *
     * 最最主要的原因,单例模式外部类不能new出对象,要使用该方法只能是静态的
     *
     * 最终返回的肯定是本类,所以该方法的类型为EnumInstance
     */
    public static EnumInstance getInstane(){
        return INSTANCE;
    }

}

 

 

测试类:

package com.geely.design.pattern.creational.singleton;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {
    /**
     * 序列化代码演练
     * 将 HungrySingleton对象放入文件,再从文件读取该对象,还是同一个对象吗?
     * 实际应用:在从文件存入后读取,用equals时需要注意(equals比较的是hash码)
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        try {
            //将singleton对象写入到输出流中
            EnumInstance instance = EnumInstance.getInstane();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
            oos.writeObject(instance);

            //从输入流中读取到该对象
            File file = new File("singleton_file");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            EnumInstance instance2 = (EnumInstance) ois.readObject();
            System.out.println(instance);
            System.out.println(instance2);
            System.out.println(instance == instance2);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
}

 

打印日志:

"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=42967:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test
INSTANCE
INSTANCE
true

Process finished with exit code 0

 

 

2.2  枚举类单例解决序列化破坏原理

ObjectInputStream

 

/**
     * Underlying readObject implementation.
     */
    private Object readObject0(boolean unshared) throws IOException {
        boolean oldMode = bin.getBlockDataMode();
        if (oldMode) {
            int remain = bin.currentBlockRemaining();
            if (remain > 0) {
                throw new OptionalDataException(remain);
            } else if (defaultDataEnd) {
                /*
                 * Fix for 4360508: stream is currently at the end of a field
                 * value block written via default serialization; since there
                 * is no terminating TC_ENDBLOCKDATA tag, simulate
                 * end-of-custom-data behavior explicitly.
                 */
                throw new OptionalDataException(true);
            }
            bin.setBlockDataMode(false);
        }

        byte tc;
        while ((tc = bin.peekByte()) == TC_RESET) {
            bin.readByte();
            handleReset();
        }

        depth++;
        try {
            switch (tc) {
                case TC_NULL:
                    return readNull();

                case TC_REFERENCE:
                    return readHandle(unshared);

                case TC_CLASS:
                    return readClass(unshared);

                case TC_CLASSDESC:
                case TC_PROXYCLASSDESC:
                    return readClassDesc(unshared);

                case TC_STRING:
                case TC_LONGSTRING:
                    return checkResolve(readString(unshared));

                case TC_ARRAY:
                    return checkResolve(readArray(unshared));
           /*
                 * 注意:读取enum
                 *
                 */
case TC_ENUM: return checkResolve(readEnum(unshared)); case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared)); case TC_EXCEPTION: IOException ex = readFatalException(); throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA: case TC_BLOCKDATALONG: if (oldMode) { bin.setBlockDataMode(true); bin.peek(); // force header read throw new OptionalDataException( bin.currentBlockRemaining()); } else { throw new StreamCorruptedException( "unexpected block data"); } case TC_ENDBLOCKDATA: if (oldMode) { throw new OptionalDataException(true); } else { throw new StreamCorruptedException( "unexpected end of block data"); } default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } finally { depth--; bin.setBlockDataMode(oldMode); } } /** * 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 { 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);//通过readString方法获取到枚举对象的名称 Enum en = null; Class cl = desc.forClass(); if (cl != null) { try { en = Enum.valueOf(cl, name);//通过类型和名称获取到枚举常量,枚举中的name是唯一的,并且对应一个枚举常量,所以拿到的肯定是唯一的对象。 } catch (IllegalArgumentException ex) { throw (IOException) new InvalidObjectException( "enum constant " + name + " does not exist in " + cl).initCause(ex); } if (!unshared) { handles.setObject(enumHandle, en); } } handles.finish(enumHandle); passHandle = enumHandle; return en; }

 

 

 

2.3  枚举类单例解决反射攻击demo

枚举类:(同2.1)

package com.geely.design.pattern.creational.singleton;

/**
 * 这个类是enum类型
 */
public enum  EnumInstance {
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
/**
     * Java类中的静态变量在程序运行期间,其内存空间对所有该类的对象实例而言是共享的,有些时候可以认为是全局变量。
     * 因此在某些时候为了节省系统内存开销、共享资源,可以将类中的一些变量声明为静态变量!
     *
     * 最最主要的原因,单例模式外部类不能new出对象,要使用该方法只能是静态的
     *
     * 最终返回的肯定是本类,所以该方法的类型为EnumInstance
     */
    public static EnumInstance getInstane(){
        return INSTANCE;
    }

}

 

 

 

测试类:

 

package com.geely.design.pattern.creational.singleton;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {

    /**
     * 序列化代码演练
     * 将 HungrySingleton对象放入文件,再从文件读取该对象,还是同一个对象吗?
     * 实际应用:在从文件存入后读取,用equals时需要注意(equals比较的是hash码)
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /*try {
            //将singleton对象写入到输出流中
            EnumInstance instance = EnumInstance.getInstane();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
            oos.writeObject(instance);

            //从输入流中读取到该对象
            File file = new File("singleton_file");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            EnumInstance instance2 = (EnumInstance) ois.readObject();
            System.out.println(instance);
            System.out.println(instance2);
            System.out.println(instance == instance2);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/

        Class objectClass = EnumInstance.class;
        Constructor constructor = objectClass.getDeclaredConstructor();
        constructor.setAccessible(true);


    }
}

 

 

打印结果:

 

"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=43086:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test
Exception in thread "main" java.lang.NoSuchMethodException: com.geely.design.pattern.creational.singleton.EnumInstance.()
    at java.lang.Class.getConstructor0(Class.java:2892)
    at java.lang.Class.getDeclaredConstructor(Class.java:2058)
    at com.geely.design.pattern.creational.singleton.Test.main(Test.java:148)

Process finished with exit code 1

 

 

反射有参对象:

package com.geely.design.pattern.creational.singleton;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {
    /**
     * 序列化代码演练
     * 将 HungrySingleton对象放入文件,再从文件读取该对象,还是同一个对象吗?
     * 实际应用:在从文件存入后读取,用equals时需要注意(equals比较的是hash码)
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /*try {
            //将singleton对象写入到输出流中
            EnumInstance instance = EnumInstance.getInstane();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
            oos.writeObject(instance);

            //从输入流中读取到该对象
            File file = new File("singleton_file");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            EnumInstance instance2 = (EnumInstance) ois.readObject();
            System.out.println(instance);
            System.out.println(instance2);
            System.out.println(instance == instance2);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/

        Class objectClass = EnumInstance.class;
        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumInstance enumInstance = (EnumInstance) constructor.newInstance("dd",123);


    }
}

 

打印结果:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:521)
    at com.geely.design.pattern.creational.singleton.Test.main(Test.java:150)

Process finished with exit code 1

 

 

2.4  枚举类单例解决反射攻击原理

Enum.java

 

只有有参构造器

 

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

 

Constructor.java 

  /**
     * Uses the constructor represented by this {@code Constructor} object to
     * create and initialize a new instance of the constructor's
     * declaring class, with the specified initialization parameters.
     * Individual parameters are automatically unwrapped to match
     * primitive formal parameters, and both primitive and reference
     * parameters are subject to method invocation conversions as necessary.
     *
     * 

If the number of formal parameters required by the underlying constructor * is 0, the supplied {@code initargs} array may be of length 0 or null. * *

If the constructor's declaring class is an inner class in a * non-static context, the first argument to the constructor needs * to be the enclosing instance; see section 15.9.3 of * The Java™ Language Specification. * *

If the required access and argument checks succeed and the * instantiation will proceed, the constructor's declaring class * is initialized if it has not already been initialized. * *

If the constructor completes normally, returns the newly * created and initialized instance. * * @param initargs array of objects to be passed as arguments to * the constructor call; values of primitive types are wrapped in * a wrapper object of the appropriate type (e.g. a {@code float} * in a {@link java.lang.Float Float}) * * @return a new object created by calling the constructor * this object represents * * @exception IllegalAccessException if this {@code Constructor} object * is enforcing Java language access control and the underlying * constructor is inaccessible. * @exception IllegalArgumentException if the number of actual * and formal parameters differ; if an unwrapping * conversion for primitive arguments fails; or if, * after possible unwrapping, a parameter value * cannot be converted to the corresponding formal * parameter type by a method invocation conversion; if * this constructor pertains to an enum type. * @exception InstantiationException if the class that declares the * underlying constructor represents an abstract class. * @exception InvocationTargetException if the underlying constructor * throws an exception. * @exception ExceptionInInitializerError if the initialization provoked * by this method fails. */ @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(); } return (T) ca.newInstance(initargs);

 

 

 

 

 

3    jad的使用
 3.1  网址:

https://varaneckas.com/jad/

 

转载于:https://www.cnblogs.com/1446358788-qq/p/11392203.html

你可能感兴趣的:(设计模式课程 设计模式精讲 8-8 单例设计模式-Enum枚举单例、原理源码解析以及反编译实战...)