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

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

/**
 * 这个类是Enum类型
 * 这个枚举非常简单
 * 枚举类是Object
 * 他在多线程的时候呢
 * 肯定是没有问题的
 * 我们主要是关注枚举类型的序列号机制
 * 还有反射攻击
 * 那枚举类天然的可序列化机制呢
 * 能够强有力的保证不会出现多次实例化的情况
 * 那即使在复杂的序列化
 * 或者反射的攻击下
 * 枚举类型的单例模式
 * 都没有问题
 * 只不过我们现在这种写法
 * 看起来不太自然
 * 但是枚举类型的单例
 * 可能是实现单例模式中的最佳实现
 * 也是强烈推荐这种写法
 * 那我们先测试一下序列化
 * 我们把序列化相关的代码先打开
 * 
 * 
 * 
 * @author Leon.Sun
 *
 */
public enum EnumInstance {
	/**
	 * 我们声明一个instance
	 * 
	 */
    INSTANCE{
        protected  void printTest(){
            System.out.println("Geely Print Test");
        }
    };
    protected abstract void printTest();
    /**
     * 为了测试我们声明一个Object
     * 这里用Object
     * 用别的类型也可以
     * 只不过我们做测试使用
     * 
     */
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
    
    /**
     * 获取这个枚举类型
     * 
     * @return
     */
    public static EnumInstance getInstance(){
        return INSTANCE;
    }

}
package com.learn.design.pattern.creational.singleton;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        LazySingleton lazySingleton = LazySingleton.getInstance();

//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());

//        Thread t1 = new Thread(new T());
//        Thread t2 = new Thread(new T());
//        t1.start();
//        t2.start();
//        System.out.println("program end");

//        HungrySingleton instance = HungrySingleton.getInstance();
    	/**
    	 * 我们直接用EnumInstance获取一个instance
    	 * 等于一个什么呢
    	 * EnumInstance.getInstance()
    	 * 
    	 * 
    	 */
        EnumInstance instance = EnumInstance.getInstance();
        /**
         * 我们直接setData
         * new一个Object
         * 也就是说枚举类持有的对象在这一行已经new好了
         * 然后把它序列化
         * 再反序列化回来
         * 看看这个data是不是同一个
         * 
         * 
         */
        instance.setData(new Object());

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oos.writeObject(instance);

        File file = new File("singleton_file");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

//        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
        /**
         * 这里替换成枚举类型EnumInstance
         * 
         * 
         */
        EnumInstance newInstance = (EnumInstance) ois.readObject();

        /**
         * 输出的时候是两个instance
         * 并且他们是相等的
         * 那这些我们一会通过反编译来看
         * 我们其实用的是枚举类持有的对象
         * 那我们现在就用data来做实验
         * 
         * 所以我们这里边直接打印data
         * 
         * 第一个输出原始的instance的data
         * java.lang.Object@3b07d329
         * 
         * 
         */
        System.out.println(instance.getData());
        /**
         * 反序列化回来的对象
         * 也是java.lang.Object@3b07d329
         * 他们想不想等呢
         * 答案是true
         * 那枚举类就是这么强大
         * 那我们看一下序列号和反序列号对枚举是怎么处理的呢
         * 我们打开ObjectInputStream这个类
         * 
         */
        System.out.println(newInstance.getData());
        /**
         * 这个也是进行一个比较
         * 注意它是object类型
         * 我们直接run一下
         * 结果出来了
         * 我们一起来看一下
         * 
         * 
         */
        System.out.println(instance.getData() == newInstance.getData());

//        Class objectClass = HungrySingleton.class;
//        Class objectClass = StaticInnerClassSingleton.class;

//        Class objectClass = LazySingleton.class;
//        Class objectClass = EnumInstance.class;

//        Constructor constructor = objectClass.getDeclaredConstructor();
//        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
//        constructor.setAccessible(true);
//        EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);


//
//        LazySingleton newInstance = (LazySingleton) constructor.newInstance();
//        LazySingleton instance = LazySingleton.getInstance();



//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
//        StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();

//        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
//        HungrySingleton instance = HungrySingleton.getInstance();

//        System.out.println(instance);
//        System.out.println(newInstance);
        
//        System.out.println(instance == newInstance);

//        EnumInstance instance = EnumInstance.getInstance();
//        instance.printTest();


    }
}
我们打开ObjectOutputStream这个类,这里面有一个方法叫readEnum

    /**
     * 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);
        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;
    }
	
看一下这个方法,我们看一下readEnum的依赖,那前面我们讲序列号和反序列号的时候呢,

讲的是checkResolve(readOrdinaryObject(unshared))
	
    /**
     * 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++;
        totalObjectRefs++;
        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));

                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);
        }
    }
	
现在我们来看一下readEnum,进来private Enum readEnum(boolean unshared),这个方法上面都是各种校验,

观察重点的东西,看一下String name = readString(false);行,通过readString这个方法呢,获取枚举对象的名称name,

然后看一下行,Enum result = null;Enum声明成null,Enum en = Enum.valueOf((Class)cl, name);这一行进行赋值,

再通过这个类型和name来获取枚举常量,那因为枚举中的name唯一的,并且对应一个枚举常量,所以这里拿到的肯定是唯一的枚举

常量,这样就没有创建新的对象,维持了这个对象的单例属性,那ObjectInputStream里边,关于枚举处理的一个方法,还是比较简单的,

而且非常容易理解,所以枚举类对于序列号这种破坏,是不受影响的,那我们接着回来,Test里面,那枚举类不受影响,直接看一下反射呢
package com.learn.design.pattern.creational.singleton;

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

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        LazySingleton lazySingleton = LazySingleton.getInstance();

//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());

//        Thread t1 = new Thread(new T());
//        Thread t2 = new Thread(new T());
//        t1.start();
//        t2.start();
//        System.out.println("program end");

//        HungrySingleton instance = HungrySingleton.getInstance();
//        EnumInstance instance = EnumInstance.getInstance();
//        instance.setData(new Object());

//        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
//        oos.writeObject(instance);

//        File file = new File("singleton_file");
//        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

//        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
//        EnumInstance newInstance = (EnumInstance) ois.readObject();

//        System.out.println(instance.getData());
//        System.out.println(newInstance.getData());
//        System.out.println(instance.getData() == newInstance.getData());

//        Class objectClass = HungrySingleton.class;
//        Class objectClass = StaticInnerClassSingleton.class;

//        Class objectClass = LazySingleton.class;
        Class objectClass = EnumInstance.class;

        /**
         * 现在已经报异常了
         * 报的是NoSuchException
         * 那这个异常就比较有意思了
         * 那这个异常就出现在objectClass.getDeclaredConstructor();
         * 就是获取构造器的时候呢
         * 并没有获得无参构造器
         * 那为什么没有获得无参构造器呢
         * 那这个困惑我们现在就来解决
         * 看一下java.lang.Enum
         * 
         * 
         */
        Constructor constructor = objectClass.getDeclaredConstructor();
//        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
        constructor.setAccessible(true);
        EnumInstance instance = (EnumInstance) constructor.newInstance();
//        EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);


//
//        LazySingleton newInstance = (LazySingleton) constructor.newInstance();
//        LazySingleton instance = LazySingleton.getInstance();



//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
//        StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();

//        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
//        HungrySingleton instance = HungrySingleton.getInstance();

//        System.out.println(instance);
//        System.out.println(newInstance);
        
//        System.out.println(instance == newInstance);

//        EnumInstance instance = EnumInstance.getInstance();
//        instance.printTest();


    }
}
枚举类的源码最大化一下

public abstract class Enum>
        implements Comparable, Serializable 
		
我们看一下这个类是抽象类,往下看,这些都是声明,注意看这里

/**
 * Sole constructor.  Programmers cannot invoke this constructor.
 * It is for use by code emitted by the compiler in response to
 * enum type declarations.
 *
 * @param name - The name of this enum constant, which is the identifier
 *               used to declare it.
 * @param ordinal - The ordinal of this enumeration constant (its position
 *         in the enum declaration, where the initial constant is assigned
 *         an ordinal of zero).
 */
protected Enum(String name, int ordinal) {
	this.name = name;
	this.ordinal = ordinal;
}

这个方法就是Enum的构造方法,我们再看一下还有别的方法吗,已经没有了,也就是说Enum这个类呢,

只有一个构造器,并且这个不是无参的,而是需要传两个参数,才能构造出来,那我们就在Test里面不调用他的无参构造器,

我们直接按照构造器这个类型直接传进去,一个String,一个Int

Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
package com.learn.design.pattern.creational.singleton;

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

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        LazySingleton lazySingleton = LazySingleton.getInstance();

//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());

//        Thread t1 = new Thread(new T());
//        Thread t2 = new Thread(new T());
//        t1.start();
//        t2.start();
//        System.out.println("program end");

//        HungrySingleton instance = HungrySingleton.getInstance();
//        EnumInstance instance = EnumInstance.getInstance();
//        instance.setData(new Object());

//        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
//        oos.writeObject(instance);

//        File file = new File("singleton_file");
//        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

//        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
//        EnumInstance newInstance = (EnumInstance) ois.readObject();

//        System.out.println(instance.getData());
//        System.out.println(newInstance.getData());
//        System.out.println(instance.getData() == newInstance.getData());

//        Class objectClass = HungrySingleton.class;
//        Class objectClass = StaticInnerClassSingleton.class;

//        Class objectClass = LazySingleton.class;
        Class objectClass = EnumInstance.class;

//        Constructor constructor = objectClass.getDeclaredConstructor();
        /**
         * 那这一行肯定OK了
         * 我们再run一下
         * 先不进行对比
         * 先run起来
         * 看一下行不行
         * 果然这一行就过了
         * 
         * 
         */
        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
        constructor.setAccessible(true);
//        EnumInstance instance = (EnumInstance) constructor.newInstance();
        /**
         * 这个时候我们就可以调用构造器的newInstance方法
         * 然后第一个传String"Geely"
         * 第二个数字666
         * 然后前面用EnumInstance接收一下
         * 强转
         * 
         * 在这一行的时候出现了异常
         * 而这个异常看一下
         * IllegalArgumentException
         * 非法的参数异常
         * 后面还有说明
         * Cannot reflectively create enum objects
         * 不能反射去创建对象
         * 这个说明说的非常明显
         * 鼠标放到这
         * at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
         * 构造器的417行
         * newInstance这个方法里边
         * 抛出来的
         * 那我们就进newInstance里边看一下
         * 
         * 
         * 
         */
        EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);


//
//        LazySingleton newInstance = (LazySingleton) constructor.newInstance();
//        LazySingleton instance = LazySingleton.getInstance();



//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
//        StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();

//        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
//        HungrySingleton instance = HungrySingleton.getInstance();

//        System.out.println(instance);
//        System.out.println(newInstance);
        
//        System.out.println(instance == newInstance);

//        EnumInstance instance = EnumInstance.getInstance();
//        instance.printTest();


    }
}
刚刚有提示417行

    @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;
    }
	
找到417行,throw new IllegalArgumentException("Cannot reflectively create enum objects");

if ((clazz.getModifiers() & Modifier.ENUM) != 0)这里就是判断他是不是枚举类型,看一下我们要使用newInstance这个

目标类,是不是枚举类型,所以对于枚举类型,通过反射,失败了,无法进行反射攻击,接着回来,目前来看枚举还是非常强大的,

那刚才说的序列号相关的,反射相关的,都是其他类来处理的,也就是说ObjectInptuStream,还有构造器这个类,他们两处理枚举类的逻辑,

那枚举类本身又什么优势呢,通过EnumInstance,我们看不出来什么,那没有关系,现在分享一个非常有用的反编译工具,JAD,网址就是

这个网址: https://varaneckas.com/jad/

单例设计模式-Enum枚举单例、原理源码解析以及反编译实战_第1张图片

这个网址: https://varaneckas.com/jad/,可以登陆这个网站,来下载JAD,这里面有不同系统的版本,

linux,windows,mac都可以下载到,那下载完成之后呢,下载完之后呢是一个解压包,只要解压就可以使用了,

那来到命令行,一起来操作一下,我已经下载好了,一起来看一下,为了方便我配置了环境变量,把jad放到了path里边,

我们看一下编译出来的目录会在哪里,这里有一个target,我们找到对应的目录,

C:\JavaEE_Workspace\demo\target\classes\com\learn\design\pattern\creational\singleton

这里有一个EnumInstance.class,用JAD来反编译这个class,直接右键copy path,

jad C:\JavaEE_Workspace\demo\target\classes\com\learn\design\pattern\creational\singleton\EnumInstance.class

非常简单,这个命令还有一些参数,可以上网查一下,或者看官方的帮助文档,我们就用最简单的命令来反编译他,我们可以看到反编译

已经完成了

单例设计模式-Enum枚举单例、原理源码解析以及反编译实战_第2张图片

生成了EnumInstance.jad,我们直接进来看一下,因为字体比较大,

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumInstance.java

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


public final class EnumInstance extends Enum
{

    private EnumInstance(String s, int i)
    {
        super(s, i);
    }

    public Object getData()
    {
        return data;
    }

    public void setData(Object data)
    {
        this.data = data;
    }

    public static EnumInstance getInstance()
    {
        return INSTANCE;
    }

    public static EnumInstance[] values()
    {
        EnumInstance aenuminstance[];
        int i;
        EnumInstance aenuminstance1[];
        System.arraycopy(aenuminstance = ENUM$VALUES, 0, aenuminstance1 = new EnumInstance[i = aenuminstance.length], 0, i);
        return aenuminstance1;
    }

    public static EnumInstance valueOf(String s)
    {
        return (EnumInstance)Enum.valueOf(com/learn/design/pattern/creational/singleton/EnumInstance, s);
    }

    public static final EnumInstance INSTANCE;
    private Object data;
    private static final EnumInstance ENUM$VALUES[];

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

我们一点点看吧,首先我们看一下这个类是final的,这个比较有意思,我们在代码里面是根本就看不出来的,

那继续往下看,

    private EnumInstance(String s, int i)
    {
        super(s, i);
    }
	
这个是比较符合单例模式的要求的,私有构造器,接着往下看,

    public Object getData()
    {
        return data;
    }

    public void setData(Object data)
    {
        this.data = data;
    }

这个是data的get/set方法,继续,

    public static EnumInstance getInstance()
    {
        return INSTANCE;
    }
	
这个就是我们写的getInstance方法,继续往下看,

public static final EnumInstance INSTANCE;我们可以看到这个INSTANCE对象,是一个static,并且呢,final的,

那么他在什么时候实例化呢,往下看,

    static 
    {
        INSTANCE = new EnumInstance("INSTANCE", 0);
        ENUM$VALUES = (new EnumInstance[] {
            INSTANCE
        });
    }
	
它是通过静态块的方式,来实例化他,所以本身Enum这个类啊,他在加载的时候,就把INSTANCE这个对象声明好了,

这里声明这个单例对象呢,和饿汉模式非常像,所以呢通过这些点,本身ENUM这个类,是非常符合单例模式的要求的,

那我们从头再看一遍,final代表这个,构造器私有这里

    private EnumInstance(String s, int i)
    {
        super(s, i);
    }
	
不允许外部进行实例化,同时类变量是静态的,INSTANCE是静态的,没有延迟初始化,通过这个静态块在类加载的时候,

把这个对象初始化完成,所以呢他也是线程安全的,那除了这些之外,还有IO相关的类,反射相关的类,来为枚举类保驾护航,

适当的抛出异常,所以枚举类实现单例模式还是比较优雅的,那关于JAD这个工具,有兴趣的可以多使用他,通过它可以发现

很多有意思的地方,在这里也推荐大家多使用JAD反编译工具,可能能帮你发现一些新大陆,对于某些代码反编译出来,

很容易豁然开朗,那接着回到代码里边,可能枚举类平时使用的不多,项目中使用枚举类也是使用的比较简单的方式,

那这个类里面本身还可以声明方法,那我们现在来领着大家来实战一下
package com.learn.design.pattern.creational.singleton;

public enum EnumInstance {
	/**
	 * 例如我们在这里加一个大括号
	 * 我们就声明一个protected方法
	 * 
	 * 
	 */
    INSTANCE{
        protected  void printTest(){
        	/**
        	 * 直接输出
        	 * 那还可以继续声明
        	 * 
        	 * 
        	 */
            System.out.println("Geely Print Test");
        }
    };
	/**
	 * 为了外部能够访问到printTest方法呢
	 * 我们还要声明一个方法
	 * 抽象方法
	 * 方法名和这个保持一致
	 * 就可以了
	 * 但是如果没有这个声明
	 * 外部是访问不了这个方法的
	 * 你们也可以试一下
	 * 讲到这里
	 * 希望你们通过JAD反编译工具
	 * 把现在写的类再反编译一次
	 * 尝试一下自己去看printTest方法到底是怎么回事
	 * 那也算留一个小作业了
	 * 相信通过这个过程
	 * 也能提高很多
	 * 至少JAD工具会用
	 * 并且也能看懂
	 * 为什么现在枚举类写方法的时候
	 * 我们要这么写
	 * 我们来到Test里边
	 * 
	 * 
	 * 
	 */
    protected abstract void printTest();
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
    
    /**
     * 获取这个枚举类型
     * 
     * @return
     */
    public static EnumInstance getInstance(){
        return INSTANCE;
    }

}
package com.learn.design.pattern.creational.singleton;

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

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        LazySingleton lazySingleton = LazySingleton.getInstance();

//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());

//        Thread t1 = new Thread(new T());
//        Thread t2 = new Thread(new T());
//        t1.start();
//        t2.start();
//        System.out.println("program end");

//        HungrySingleton instance = HungrySingleton.getInstance();
//        EnumInstance instance = EnumInstance.getInstance();
//        instance.setData(new Object());

//        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
//        oos.writeObject(instance);

//        File file = new File("singleton_file");
//        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

//        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
//        EnumInstance newInstance = (EnumInstance) ois.readObject();

//        System.out.println(instance.getData());
//        System.out.println(newInstance.getData());
//        System.out.println(instance.getData() == newInstance.getData());

//        Class objectClass = HungrySingleton.class;
//        Class objectClass = StaticInnerClassSingleton.class;

//        Class objectClass = LazySingleton.class;
//        Class objectClass = EnumInstance.class;

//        Constructor constructor = objectClass.getDeclaredConstructor();
        /**
         * 那这一行肯定OK了
         * 我们再run一下
         * 先不进行对比
         * 先run起来
         * 看一下行不行
         * 果然这一行就过了
         * 
         * 
         */
//        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
//        constructor.setAccessible(true);
//        EnumInstance instance = (EnumInstance) constructor.newInstance();
        /**
         * 这个时候我们就可以调用构造器的newInstance方法
         * 然后第一个传String"Geely"
         * 第二个数字666
         * 然后前面用EnumInstance接收一下
         * 强转
         * 
         * 在这一行的时候出现了异常
         * 而这个异常看一下
         * IllegalArgumentException
         * 非法的参数异常
         * 后面还有说明
         * Cannot reflectively create enum objects
         * 不能反射去创建对象
         * 这个说明说的非常明显
         * 鼠标放到这
         * at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
         * 构造器的417行
         * newInstance这个方法里边
         * 抛出来的
         * 那我们就进newInstance里边看一下
         * 
         * 
         * 
         */
//        EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);


//
//        LazySingleton newInstance = (LazySingleton) constructor.newInstance();
//        LazySingleton instance = LazySingleton.getInstance();



//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
//        StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();

//        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
//        HungrySingleton instance = HungrySingleton.getInstance();

//        System.out.println(instance);
//        System.out.println(newInstance);
        
//        System.out.println(instance == newInstance);

        EnumInstance instance = EnumInstance.getInstance();
        /**
         * 结果已经出来了
         * 这个方法已经执行了
         * 那我们通过学习枚举类呢
         * 你一定能够提高很多
         * 那关于我们单例模式
         * 这里重点说一下
         * 单例模式实现的方案很多
         * 每一种都有自己的优缺点
         * 所以我们要根据不同的业务场景来选择自己的单例模式
         * 还有这种模式是如何演进的
         * 这几种单例方式都解决了什么问题
         * 他又存在什么问题
         * 如果问设计模式的话
         * 百分之九十九都会问单例这个模式
         * 单例模式看起来非常的简单
         * 对于初学者来说他也是非常简单的
         * 但是中间就是隔着一层窗户纸
         * 只要一捅破
         * 单例模式是这些模式中最复杂的模式
         * 如果要深入研究的话
         * 这里面的知识点还是非常有意思的
         * 相信通过学习单例模式呢
         * 对于提高个人的思维能力
         * 编码能力
         * 实操能力
         * 这些肯定会有所提高的
         * 另外希望有一点
         * 如果学了这门课程
         * 然后单例模式也都听完了
         * 
         * 
         * 
         */
        instance.printTest();


    }
}

 

你可能感兴趣的:(枚举,单例模式)