精华内容
下载资源
问答
  • 枚举实现单例模式

    2020-10-31 14:01:08
    枚举实现单例模式引言饿汉单例懒汉单例枚举单例破解饿汉单例破解懒汉单例枚举实现单例的安全性引申 引言 饿汉单例、懒汉单例,最优的枚举单例 饿汉单例 饿汉单例实现: public class SingleTest { public static ...

    引言

    饿汉单例、懒汉单例,最优的枚举单例

    饿汉单例

    饿汉单例实现:

    public class SingleTest {
        public static void main(String[] args) {
            System.out.println(SingleTon.getInstance());
            System.out.println(SingleTon.getInstance());
            System.out.println(SingleTon.getInstance() == SingleTon.getInstance());
        }
    }
    
    /**
     * 饿汉单例
     */
    class SingleTon {
        /**
         * 静态变量引用实例对象,独一份
         */
        private static SingleTon instance = new SingleTon();
    
        /**
         * 防止外部实例化
         */
        private SingleTon() {
        }
    
        /**
         * 公共的对象获取方法
         */
        public static SingleTon getInstance() {
            return instance;
        }
    }
    

    运行结果:

    com.juc.juc_test.SingleTon@816f27d
    com.juc.juc_test.SingleTon@816f27d
    true
    

    懒汉单例

    懒汉单例实现:

    public class LazySingleTest {
        public static void main(String[] args) {
            System.out.println(LazySingle.getInstance());
            System.out.println(LazySingle.getInstance());
            System.out.println(LazySingle.getInstance() == LazySingle.getInstance());
        }
    }
    
    class LazySingle {
        /**
         * volatile解决内存可见性问题,防止指令重新排序
         */
        private volatile static LazySingle instance;
    
        /**
         * 防止外部实例化
         */
        private LazySingle() {
        }
    
        /**
         * 公共获取对象的方法
         * 双检锁实现单例
         *
         * @return 单例对象
         */
        public static Object getInstance() {
            if (Objects.isNull(instance)) {
                synchronized (LazySingleTest.class) {
                    if (Objects.isNull(instance)) {
                        instance = new LazySingle();
                    }
                }
            }
            return instance;
        }
    }
    

    运行结果:

    com.juc.juc_test.LazySingle@816f27d
    com.juc.juc_test.LazySingle@816f27d
    true
    

    枚举单例

    枚举单例实现:

    public class SingleTest {
        public static void main(String[] args) {
            System.out.println(SingleEnum.SINGLE_OBJECT.object);
            System.out.println(SingleEnum.SINGLE_OBJECT.object);
            System.out.println(SingleEnum.SINGLE_OBJECT.object == SingleEnum.SINGLE_OBJECT.object);
        }
    }
    
    /**
     * 枚举
     */
    enum SingleEnum {
        /**
         * 枚举实例
         */
        SINGLE_OBJECT;
    
        /**
         * 单例对象
         */
        public final Object object;
    
        /**
         * 构造方法
         */
        SingleEnum() {
            object = new Object();
        }
    }
    

    破解饿汉单例

    反射破解:

    public class SingleTest {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            // 创建构造器对象
            Constructor<SingleTon> constructor = SingleTon.class.getDeclaredConstructor();
            // 暴力反射
            constructor.setAccessible(true);
            // 创建实例
            SingleTon singleTon = constructor.newInstance();
            // 使用公共方法获取单例对象
            System.out.println(SingleTon.getInstance());
            System.out.println(singleTon);
            System.out.println(SingleTon.getInstance() == singleTon);
        }
    }
    
    /**
     * 饿汉单例
     */
    class SingleTon {
    
        /**
         * 静态变量引用实例对象,独一份
         */
        private static SingleTon instance = new SingleTon();
    
        /**
         * 防止外部实例化
         */
        private SingleTon() {
        }
    
        /**
         * 公共的对象获取方法
         */
        public static SingleTon getInstance() {
            return instance;
        }
    }
    

    运行结果:

    com.juc.juc_test.SingleTon@816f27d
    com.juc.juc_test.SingleTon@87aac27
    false
    

    可以看出,暴力反射创建的SingleTon对象和通过公共方法获取到的SingleTon对象已经不是同一个对象

    破解懒汉单例

    反射破解:

    public class LazySingleTest {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            // 创建构造器对象
            Constructor<LazySingle> constructor = LazySingle.class.getDeclaredConstructor();
            // 暴力反射
            constructor.setAccessible(true);
            // 创建实例
            LazySingle lazySingle = constructor.newInstance();
            // 使用公共方法获取单例对象
            System.out.println(LazySingle.getInstance());
            System.out.println(lazySingle);
            System.out.println(LazySingle.getInstance() == lazySingle);
        }
    }
    
    class LazySingle {
        /**
         * volatile解决内存可见性问题,防止指令重新排序
         */
        private volatile static LazySingle instance;
    
        /**
         * 防止外部实例化
         */
        private LazySingle() {
        }
    
        /**
         * 公共获取对象的方法
         * 双检锁实现单例
         *
         * @return 单例对象
         */
        public static Object getInstance() {
            if (Objects.isNull(instance)) {
                synchronized (LazySingleTest.class) {
                    if (Objects.isNull(instance)) {
                        instance = new LazySingle();
                    }
                }
            }
            return instance;
        }
    }
    

    运行结果:

    com.juc.juc_test.LazySingle@816f27d
    com.juc.juc_test.LazySingle@87aac27
    false
    

    可以看出,暴力反射创建的LazySingle对象和通过公共方法获取到的LazySingle对象已经不是同一个对象

    枚举实现单例的安全性

    无论是懒汉单例还是饿汉单例,都能够通过反射的方式破解,但是枚举实现的单例,当你去反射破解的时候会发现:
    反射枚举报错

    引申

    饿汉、懒汉可以在构造器中抛出异常,防止反射破解

    展开全文
  • 通过枚举实现单例模式 对于饿汉式单例模式和懒汉式单例模式了解的同学,使用以上两种单例模式,我们均可以通过反射的方式破坏单例模式的特性。那么,如何才能避免程序开发中使用反射的方式破坏单例的这种行为呢?接...

    通过枚举实现单例模式

    对于饿汉式单例模式和懒汉式单例模式了解的同学,使用以上两种单例模式,我们均可以通过反射的方式破坏单例模式的特性。那么,如何才能避免程序开发中使用反射的方式破坏单例的这种行为呢?接下来用过枚举的方式来创建的单例模式就可以避免上述的问题。

    直接上代码:

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

    我们尝试使用反射的方式创建实例,看是否能够创建成功:

    public class EnumSingletonTest {
        public static void main(String[] args) {
            EnumSingleton enumSingleton=EnumSingleton.getInstance();
            enumSingleton.setData(new Object());
            System.out.println("创建单例模式:"+enumSingleton);
            try{
                Class clazz= EnumSingleton.class;
                Constructor c=clazz.getDeclaredConstructor(String.class,int.class);
                c.setAccessible(true);
                Object object=c.newInstance();
                System.out.println(object);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    会出现以下报错:

    java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    	at com.example.singleton.EnumSingletonTest.main(EnumSingletonTest.java:16)
    

    我们直接定位到报错源码的位置(Constructor.java:417)发现如下内容:

        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");
    

    也就是说,在通过反射调用方法newInstance()创建实例时,源码会对class的修饰符做一个判断(clazz.getModifiers() & Modifier.ENUM) != 0)。如果创建实例的类是用enum修饰的,那么不能通过反射创建实例(Cannot reflectively create enum objects)

    展开全文
  • Java 利用枚举实现单例模式

    万次阅读 多人点赞 2016-08-24 23:50:15
    引言单例模式比较常见的实现方法有懒汉模式,DCL模式公有静态成员等,从Java 1.5版本起,单元素枚举实现单例模式成为最佳的方法。Java枚举基本用法枚举的用法比较多,本文主要旨在介绍利用枚举实现单例模式的原理,...

    引言

    单例模式比较常见的实现方法有懒汉模式,DCL模式公有静态成员等,从Java 1.5版本起,单元素枚举实现单例模式成为最佳的方法。


    Java枚举

    基本用法

    枚举的用法比较多,本文主要旨在介绍利用枚举实现单例模式的原理,所以这里也主要介绍一些相关的基础内容。
    首先,枚举类似类,一个枚举可以拥有成员变量,成员方法,构造方法。先来看枚举最基本的用法:

    enum Type{
        A,B,C,D;
    }

    创建enum时,编译器会自动为我们生成一个继承自java.lang.Enum的类,我们上面的enum可以简单看作:

    class Type extends Enum{
        public static final Type A;
        public static final Type B;
        ...
    }

    对于上面的例子,我们可以把Type看作一个类,而把A,B,C,D看作类的Type的实例。
    当然,这个构建实例的过程不是我们做的,一个enum的构造方法限制是private的,也就是不允许我们调用。

    “类”方法和“实例”方法

    上面说到,我们可以把Type看作一个类,而把A,B。。。看作Type的一个实例。同样,在enum中,我们可以定义类和实例的变量以及方法。看下面的代码:

    enum Type{
        A,B,C,D;
    
        static int value;
        public static int getValue() {
            return value;
        }
    
        String type;
        public String getType() {
            return type;
        }
    }
    
    在原有的基础上,添加了类方法和实例方法。我们把Type看做一个类,那么enum中静态的域和方法,都可以视作类方法。和我们调用普通的静态方法一样,这里调用类方法也是通过  Type.getValue()即可调用,访问类属性也是通过Type.value即可访问。
    下面的是实例方法,也就是每个实例才能调用的方法。那么实例是什么呢?没错,就是A,B,C,D。所以我们调用实例方法,也就通过 Type.A.getType()来调用就可以了。
    最后,对于某个实例而言,还可以实现自己的实例方法。再看下下面的代码:
    
    enum Type{
    A{
        public String getType() {
            return "I will not tell you";
        }
    },B,C,D;
    static int value;
    
    public static int getValue() {
        return value;
    }
    
    String type;
    public String getType() {
        return type;
     }
    }
    

    这里,A实例后面的{…}就是属于A的实例方法,可以通过覆盖原本的方法,实现属于自己的定制。
    除此之外,我们还可以添加抽象方法在enum中,强制ABCD都实现各自的处理逻辑:

    enum Type{
        A{
            public String getType() {
                return "A";
            }
        },B {
            @Override
            public String getType() {
                return "B";
            }
        },C {
            @Override
            public String getType() {
                return "C";
            }
        },D {
            @Override
            public String getType() {
                return "D";
            }
        };
    
        public abstract String getType();
    }

    枚举单例

    有了上面的基础,我们可以来看一下枚举单例的实现方法:

    class Resource{
    }
    
    public enum SomeThing {
        INSTANCE;
        private Resource instance;
        SomeThing() {
            instance = new Resource();
        }
        public Resource getInstance() {
            return instance;
        }
    }

    上面的类Resource是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。
    获取资源的方式很简单,只要 SomeThing.INSTANCE.getInstance() 即可获得所要实例。下面我们来看看单例是如何被保证的:
    首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法,同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。
    也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。
    可以看到,枚举实现单例还是比较简单的,除此之外我们再来看一下Enum这个类的声明:

    public abstract class Enum<E extends Enum<E>>
            implements Comparable<E>, Serializable

    可以看到,枚举也提供了序列化机制。某些情况,比如我们要通过网络传输一个数据库连接的句柄,会提供很多帮助。
    最后借用 《Effective Java》一书中的话,

    单元素的枚举类型已经成为实现Singleton的最佳方法。

    展开全文
  • 1. 枚举创建单例模式 ... * 枚举实现单例模式 * 至于为什么枚举能实现单例模式,有兴趣的伙伴可以深度学习一下 * SpirngIOC是单例 是枚举的升级,是懒汉模式 */ public enum EnumSingleton { /** * *...

    1. 枚举创建单例模式

    import java.lang.reflect.Constructor;
    
    /**
     * @author xiyou
     * 枚举实现单例模式
     * 至于为什么枚举能实现单例模式,有兴趣的伙伴可以深度学习一下
     * SpirngIOC是单例 是枚举的升级,是懒汉模式
     */
    public enum EnumSingleton {
    
        /**
         *
         */
        INSTANCE;
        private Object data;
    
        private Object getData() {
            return data;
        }
    
        private void setData(Object data) {
            this.data = data;
        }
    
        private static EnumSingleton getInstance() {
            return INSTANCE;
        }
    
    
        public static void main(String[] args) {
            /**
             *         System.out.println(EnumSingleton.getInstance());
             *         System.out.println(EnumSingleton.getInstance());
             *         System.out.println(EnumSingleton.getInstance());
             *         System.out.println(EnumSingleton.getInstance());
             */
            Object instance = EnumSingleton.getInstance();
            System.out.println(instance);
            //通过非正常手段,反射来破坏单例
            Class<?> oneClass = EnumSingleton.class;
            try {
                Constructor c = oneClass.getDeclaredConstructor(String.class, int.class);
                c.setAccessible(true);
                Object instance1 = c.newInstance("xiyou", 666);
                Object instance2 = c.newInstance("xiyou2", 6662);
                System.out.println(instance1);
                System.out.println(instance2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    
    

    2. 测试反射破坏单例的结果

    发现不能破坏,结果如下:

    INSTANCE
    java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    
    展开全文
  • Java枚举实现单例模式

    2017-06-22 14:15:25
    Java 利用枚举实现单例模式  http://blog.csdn.net/yy254117440/article/details/52305175 Java枚举 基本用法 枚举的用法比较多,本文主要旨在介绍利用枚举实现单例模式的原理,所以这里也主要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,070
精华内容 828
关键字:

枚举实现单例模式