设计模式-注册式单例1-枚举式单例

注册式单例

1.枚举式单例

注册式单例又称为登记式单例,就是将每一个实例都登记到某一个地方,使用唯一的标 识获取实例。注册式单例有两种写法:一种为容器缓存,一种为枚举登记。先来看枚举 式单例的写法,来看代码,创建 EnumSingleton 类

package com.gupaoedu.vip.pattern.singleton.register;

/**
 * 注册式单例方式1:枚举式单例
 */
public enum EnumSingleton {
    INSTANCE;
    private  Object data;

    public Object getData() {
        return data;
    }

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

    public static  EnumSingleton getInstance(){
        return INSTANCE;
    }

}

测试:

package com.gupaoedu.vip.pattern.singleton.register;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class EnumSingletonTest {

    public static void main(String[] args) {
        EnumSingleton s1 = null;
        EnumSingleton s2 = EnumSingleton.getInstance();
        s2.setData(new Object());
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("EnumSingleton.obj")        ;
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();
            FileInputStream fis = new FileInputStream("EnumSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (EnumSingleton)ois.readObject();
            System.out.println(s1.getData());
            System.out.println(s2.getData());
            System.out.println(s1 == s2);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

输出:
在这里插入图片描述
原理探究:反编译EnumSingleton.class

package com.gupaoedu.vip.pattern.singleton.register;

public final class EnumSingleton extends Enum
{

    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }

    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(com/gupaoedu/vip/pattern/singleton/register/EnumSingleton, name);
    }

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

    public Object getData()
    {
        return data;
    }

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

    public static EnumSingleton getInstance()
    {
        return INSTANCE;
    }

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

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

发现枚举式单例的实例,其实是在静态块里面的,也就是饿汉式单例,这样绝对保证了线程安全;那么序列化能否破坏枚举式单例呢?
是回到 ObjectInputStream 的 readObject0()方法
在这里插入图片描述
readObject0()中调用了 readEnum()方法,来看 readEnum()中代码实现:
在这里插入图片描述
我们发现枚举类型其实通过类名和 Class 对象类找到一个唯一的枚举对象。因此,枚举对 象不可能被类加载器加载多次。那么反射是否能破坏枚举式单例呢?

package com.gupaoedu.vip.pattern.singleton.register;

import java.lang.reflect.Constructor;

public class EnumSingletonReflectTest {
    public static void main(String[] args) {
        try {
            Class clazz = EnumSingleton.class;
            Constructor c = clazz.getDeclaredConstructor();
            c.setAccessible(true);
            c.newInstance();
        } catch (Exception e){
            e.printStackTrace();
        }

    }
}

在这里插入图片描述
报的是 java.lang.NoSuchMethodException 异常,意思是没找到无参的构造方法。这 时候,我们打开 java.lang.Enum 的源码代码,查看它的构造方法,只有一个 protected 的构造方法,代码如下:
在这里插入图片描述
既然这样,那是否可以获取有参构造,继续通过反射破坏单例呢?

package com.gupaoedu.vip.pattern.singleton.register;

import java.lang.reflect.Constructor;

public class EnumSingletonReflectTest {

    public static void main(String[] args) {
        try {
            Class clazz = EnumSingleton.class;
            Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
            c.setAccessible(true);
            EnumSingleton obj = (EnumSingleton)c.newInstance("tom", 999);
            System.out.println(obj);
      
        } catch (Exception e){
            e.printStackTrace();
        }

    }
}

这时报如下错误:Cannot reflectively create enum objects

java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.gupaoedu.vip.pattern.singleton.test.EnumSingletonReflectTest.main(EnumSingletonReflectTest.java:26)

Process finished with exit code 0

查看构造方器的newInstance方法,如下:
在这里插入图片描述
在 newInstance()方法中做了强制性的判断,如果修饰符是 Modifier.ENUM 枚举类型, 直接抛出异常。
枚举式单例也是《Effective Java》书中推荐的一种单例实现写法。在 JDK 枚举的语法特殊性,以及反射也为枚举保 驾护航,让枚举式单例成为一种比较优雅的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值