设计模式-单例设计模式详解

生命无罪,健康万岁,我是laity。

我曾七次鄙视自己的灵魂:

第一次,当它本可进取时,却故作谦卑;

第二次,当它在空虚时,用爱欲来填充;

第三次,在困难和容易之间,它选择了容易;

第四次,它犯了错,却借由别人也会犯错来宽慰自己;

第五次,它自由软弱,却把它认为是生命的坚韧;

第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;

第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。

2023年11月21日00:22:53 - 结束

单例设计模式是一种创建型设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。这对于需要在整个应用程序中共享一些资源的情况非常有用。以下是在Java中使用单例设计模式的一些常见方法:

懒汉式(Lazy Initialization)

在第一次调用时才创建实例,如果实例已经存在,则直接返回。

package com.laity.single;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.LazySingleton
 * @Date: 2023年11月20日 22:17
 * @Description: 懒汉式单例:在第一次调用时才创建实例,如果实例已经存在,则直接返回。
 * 单例中最重要的思想是 构造器私有化,别人就不能 new这个对象,保证内存中只有一个对象
 */

public class LazySingleton {

    private LazySingleton() {
        // 私有构造函数,防止外部直接实例化
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private static LazySingleton lazySingleton;

    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

    // 单线程下 确实单例OK
    // 多线程下不可以,每次结果都不一样
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> LazySingleton.getInstance()).start();
        }
    }

}

这种方法在单线程环境中效果良好,但在多线程环境下可能出现问题,因为两个线程可能同时进入 if (instance == null) 的判断,导致创建多个实例。为了解决这个问题,可以使用加锁的方式,但这会影响性能。

public class ThreadSafeLazySingleton {
    private static ThreadSafeLazySingleton instance;

    private ThreadSafeLazySingleton() {
        // 私有构造函数,防止外部直接实例化
    }

    public static synchronized ThreadSafeLazySingleton getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazySingleton();
        }
        return instance;
    }
}

双重检查锁定(Double-Checked Locking)

在懒汉式的基础上,使用双重检查来减小锁的粒度,提高性能。

package com.laity.single;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.DoubleCheckedLockingSingleton
 * @Date: 2023年11月20日 22:31
 * @Description: 双重检查锁定:解决懒汉式单例在多线程下的问题
 * 双重检测锁模式的 懒汉式单例,DCL懒汉式
 */

public class DoubleCheckedLockingSingleton {

    private DoubleCheckedLockingSingleton() {
        // 私有构造函数,防止外部直接实例化
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private static volatile DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;

    public static DoubleCheckedLockingSingleton getInstance() {
        if (doubleCheckedLockingSingleton == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (doubleCheckedLockingSingleton == null) {
                    // 不是一个原子性操作,
                    /*
                    不是原子性操作会经过哪些步骤?
                    1.分配内存空间
                    2.执行构造方法,初始化对象
                    3.把对象指向内存空间,才能保证这个对象new完了
                    所以在多线程的情况下,可能会出现指令重排现象
                    就是原本 123 顺序,可事实确实 132 顺序,执行的顺序不一致

                    线程1:执行的顺序是 132
                    在线程1执行到3时,进来了一个线程2,线程2一看都执行到3了,
                    导致doubleCheckedLockingSingleton!=null,直接return doubleCheckedLockingSingleton;
                    但是其实此时这个对象还没有完成构造,那么这个对象就是虚无的

                    所以必须在 private static DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;
                    基础上,添加volatile
                     */
                    doubleCheckedLockingSingleton = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return doubleCheckedLockingSingleton;
    }

    // 多线程下测试
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> DoubleCheckedLockingSingleton.getInstance()).start();
        }
    }
}

使用 volatile 修饰 instance 可以确保在多线程环境下,一个线程对 instance 的修改对其他线程是可见的。

饿汉式(Eager Initialization)

在类加载时就创建实例,无论是否需要使用。

package com.laity.single;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.EagerSingleton
 * @Date: 2023年11月20日 22:22
 * @Description: 饿汉式单例:在类加载时就创建实例,无论是否需要使用。
 * 单例中最重要的思想是 构造器私有化,别人就不能 new这个对象,保证内存中只有一个对象
 */

public class EagerSingleton {

    private EagerSingleton() {
        // 私有构造函数,防止外部直接实例化
    }

    private static final EagerSingleton EAGER_SINGLETON = new EagerSingleton();

    public static EagerSingleton getInstance() {
        return EAGER_SINGLETON;
    }
}

这种方式简单,线程安全,但可能在应用程序启动时就创建实例,占用资源。

静态内部类

使用静态内部类的方式,结合懒汉式的延迟加载和线程安全性。

public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {
        // 私有构造函数,防止外部直接实例化
    }

    private static class SingletonHolder {
        private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
    }

    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.instance;
    }
}

这种方式兼具懒汉式的延迟加载和线程安全性,而且通过静态内部类的方式实现,不会在类加载时就创建实例。

选择哪种方式取决于具体的应用场景和需求。如果在高并发环境下,可以考虑使用双重检查锁定或静态内部类的方式。如果希望在应用程序启动时就创建实例,可以选择饿汉式。

=====以下内容都是娱乐科普 =======

反射破坏单例模式及解决 - 炫技

破坏

package com.laity.single;

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

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.StaticInnerClassSingleton
 * @Date: 2023年11月20日 22:48
 * @Description: 静态内部类实现
 * 单例中最重要的思想是 构造器私有化,别人就不能 new这个对象,保证内存中只有一个对象
 */

public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {
    }

    public static StaticInnerClassSingleton getInstance(){
        // 调用内部类的实现
        return SingletonHolder.STATIC_INNER_CLASS_SINGLETON;
    }

    public static class SingletonHolder{
        // 内部类调用外部类
        private static final  StaticInnerClassSingleton STATIC_INNER_CLASS_SINGLETON = new StaticInnerClassSingleton();
    }

    // 但是不安全,来来来,看我炫技 - 反射!
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
        // 反射破坏单例 - 空参构造器
        Constructor<StaticInnerClassSingleton> declaredConstructor = StaticInnerClassSingleton.class.getDeclaredConstructor(null);
        // 这样就无视了私有的构造器
        declaredConstructor.setAccessible(true);
        StaticInnerClassSingleton staticInnerClassSingleton = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(staticInnerClassSingleton);
        /*
        com.laity.single.StaticInnerClassSingleton@52cc8049
        com.laity.single.StaticInnerClassSingleton@5b6f7412
        可以看出两个值是不一致的,如果按照单例来讲,两个值应该是一样的(”保证内存中只有一个对象”)
         */
        System.out.println("=======================hashCode====================");
        System.out.println(instance.hashCode());
        System.out.println(staticInnerClassSingleton.hashCode());
        /*
        1389133897
        1534030866
        也是不一样的
         */
    }
}

解决

package com.laity.single;

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

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.DoubleCheckedLockingSingleton
 * @Date: 2023年11月20日 22:31
 * @Description: 双重检查锁定:解决懒汉式单例在多线程下的问题
 * 双重检测锁模式的 懒汉式单例,DCL懒汉式
 * 双重检测锁升级为三级检测 -> 解决反射带来的破坏
 */

public class DoubleCheckedLockingSingleton {

    private DoubleCheckedLockingSingleton() {
        // 私有构造函数,防止外部直接实例化

        // 解决反射带来封装性的破坏
        synchronized (StaticInnerClassSingleton.class) {
            if (doubleCheckedLockingSingleton != null) {
                throw new RuntimeException("不要试图使用反射破坏哦!");
            }
        }

        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private static volatile DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;

    public static DoubleCheckedLockingSingleton getInstance() {
        if (doubleCheckedLockingSingleton == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (doubleCheckedLockingSingleton == null) {
                    // 不是一个原子性操作,
                    /*
                    不是原子性操作会经过哪些步骤?
                    1.分配内存空间
                    2.执行构造方法,初始化对象
                    3.把对象指向内存空间,才能保证这个对象new完了
                    所以在多线程的情况下,可能会出现指令重排现象
                    就是原本 123 顺序,可事实确实 132 顺序,执行的顺序不一致

                    线程1:执行的顺序是 132
                    在线程1执行到3时,进来了一个线程2,线程2一看都执行到3了,
                    导致doubleCheckedLockingSingleton!=null,直接return doubleCheckedLockingSingleton;
                    但是其实此时这个对象还没有完成构造,那么这个对象就是虚无的

                    所以必须在 private static DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;
                    基础上,添加volatile
                     */
                    doubleCheckedLockingSingleton = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return doubleCheckedLockingSingleton;
    }

    // 多线程下测试
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                DoubleCheckedLockingSingleton instance = DoubleCheckedLockingSingleton.getInstance();
                try {
                    Constructor<DoubleCheckedLockingSingleton> declaredConstructor = DoubleCheckedLockingSingleton.class.getDeclaredConstructor();
                    declaredConstructor.setAccessible(true);
                    DoubleCheckedLockingSingleton doubleCheckedLockingSingleton = declaredConstructor.newInstance();
                    System.out.println(instance == doubleCheckedLockingSingleton);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

再破坏再解决(红绿灯)

破坏

package com.laity.single;

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

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.DoubleCheckedLockingSingleton
 * @Date: 2023年11月20日 22:31
 * @Description: 双重检查锁定:解决懒汉式单例在多线程下的问题
 * 双重检测锁模式的 懒汉式单例,DCL懒汉式
 */

public class DoubleCheckedLockingSingleton {

    private DoubleCheckedLockingSingleton() {
        // 私有构造函数,防止外部直接实例化

        // 解决反射带来封装性的破坏
        synchronized (StaticInnerClassSingleton.class) {
            if (doubleCheckedLockingSingleton != null) {
                throw new RuntimeException("不要试图使用反射破坏哦!");
            }
        }

        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private static volatile DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;

    public static DoubleCheckedLockingSingleton getInstance() {
        if (doubleCheckedLockingSingleton == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (doubleCheckedLockingSingleton == null) {
                    // 不是一个原子性操作,
                    /*
                    不是原子性操作会经过哪些步骤?
                    1.分配内存空间
                    2.执行构造方法,初始化对象
                    3.把对象指向内存空间,才能保证这个对象new完了
                    所以在多线程的情况下,可能会出现指令重排现象
                    就是原本 123 顺序,可事实确实 132 顺序,执行的顺序不一致

                    线程1:执行的顺序是 132
                    在线程1执行到3时,进来了一个线程2,线程2一看都执行到3了,
                    导致doubleCheckedLockingSingleton!=null,直接return doubleCheckedLockingSingleton;
                    但是其实此时这个对象还没有完成构造,那么这个对象就是虚无的

                    所以必须在 private static DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;
                    基础上,添加volatile
                     */
                    doubleCheckedLockingSingleton = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return doubleCheckedLockingSingleton;
    }

    // 多线程下测试
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                // 之前我是创建了对象,那么现在我根本就不去创建这个对象
                // DoubleCheckedLockingSingleton instance = DoubleCheckedLockingSingleton.getInstance();
                try {
                    Constructor<DoubleCheckedLockingSingleton> declaredConstructor = DoubleCheckedLockingSingleton.class.getDeclaredConstructor();
                    declaredConstructor.setAccessible(true);
                    // 两个对象都是基于反射new出来的
                    DoubleCheckedLockingSingleton doubleCheckedLockingSingleton1 = declaredConstructor.newInstance();
                    DoubleCheckedLockingSingleton doubleCheckedLockingSingleton2 = declaredConstructor.newInstance();
                    System.out.println(doubleCheckedLockingSingleton1 == doubleCheckedLockingSingleton2);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

结果是:

Thread-9ok
Thread-1ok
Thread-0ok
Thread-4ok
Thread-6ok
Thread-2ok
Thread-8ok
Thread-5ok
Thread-3ok
Thread-7ok
Thread-3ok
Thread-5ok
false
Thread-8ok
false
Thread-2ok
false
Thread-6ok
false
Thread-4ok
false
Thread-0ok
false
Thread-1ok
false
Thread-9ok
false
false
Thread-7ok
false

可以看到单例模式又被破坏了

解决

package com.laity.single;

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

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.DoubleCheckedLockingSingleton
 * @Date: 2023年11月20日 22:31
 * @Description: 双重检查锁定:解决懒汉式单例在多线程下的问题
 * 双重检测锁模式的 懒汉式单例,DCL懒汉式
 */

public class DoubleCheckedLockingSingleton {

    // 红绿灯 - 如果加密处理会使其更安全
    private static boolean laity = false;

    private DoubleCheckedLockingSingleton() {
        // 私有构造函数,防止外部直接实例化

        // 解决反射带来封装性的破坏
        synchronized (StaticInnerClassSingleton.class) {
            if (!laity) {
                laity = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏哦!");
            }
        }

        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private static volatile DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;

    public static DoubleCheckedLockingSingleton getInstance() {
        if (doubleCheckedLockingSingleton == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (doubleCheckedLockingSingleton == null) {
                    // 不是一个原子性操作,
                    /*
                    不是原子性操作会经过哪些步骤?
                    1.分配内存空间
                    2.执行构造方法,初始化对象
                    3.把对象指向内存空间,才能保证这个对象new完了
                    所以在多线程的情况下,可能会出现指令重排现象
                    就是原本 123 顺序,可事实确实 132 顺序,执行的顺序不一致

                    线程1:执行的顺序是 132
                    在线程1执行到3时,进来了一个线程2,线程2一看都执行到3了,
                    导致doubleCheckedLockingSingleton!=null,直接return doubleCheckedLockingSingleton;
                    但是其实此时这个对象还没有完成构造,那么这个对象就是虚无的

                    所以必须在 private static DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;
                    基础上,添加volatile
                     */
                    doubleCheckedLockingSingleton = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return doubleCheckedLockingSingleton;
    }

    // 多线程下测试
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                // 之前我是创建了对象,那么现在我根本就不去创建这个对象
                // DoubleCheckedLockingSingleton instance = DoubleCheckedLockingSingleton.getInstance();
                try {
                    Constructor<DoubleCheckedLockingSingleton> declaredConstructor = DoubleCheckedLockingSingleton.class.getDeclaredConstructor();
                    declaredConstructor.setAccessible(true);
                    // 两个对象都是基于反射new出来的
                    DoubleCheckedLockingSingleton doubleCheckedLockingSingleton1 = declaredConstructor.newInstance();
                    DoubleCheckedLockingSingleton doubleCheckedLockingSingleton2 = declaredConstructor.newInstance();
                    System.out.println(doubleCheckedLockingSingleton1 == doubleCheckedLockingSingleton2);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.laity.single.DoubleCheckedLockingSingleton.lambda$main$0(DoubleCheckedLockingSingleton.java:75)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: 不要试图使用反射破坏哦!
	at com.laity.single.DoubleCheckedLockingSingleton.<init>(DoubleCheckedLockingSingleton.java:28)
	... 6 more

再破坏再解密(枚举)

破坏

获取到你设置的秘钥来进行破坏

package com.laity.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.DoubleCheckedLockingSingleton
 * @Date: 2023年11月20日 22:31
 * @Description: 双重检查锁定:解决懒汉式单例在多线程下的问题
 * 双重检测锁模式的 懒汉式单例,DCL懒汉式
 */

public class DoubleCheckedLockingSingleton {

    // 红绿灯 - 如果加密处理会使其更安全
    private static boolean laity = false;

    private DoubleCheckedLockingSingleton() {
        // 私有构造函数,防止外部直接实例化

        // 解决反射带来封装性的破坏
        synchronized (StaticInnerClassSingleton.class) {
            if (!laity) {
                laity = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏哦!");
            }
        }

        System.out.println(Thread.currentThread().getName() + "-ok");
    }

    private static volatile DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;

    public static DoubleCheckedLockingSingleton getInstance() {
        if (doubleCheckedLockingSingleton == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (doubleCheckedLockingSingleton == null) {
                    // 不是一个原子性操作,
                    /*
                    不是原子性操作会经过哪些步骤?
                    1.分配内存空间
                    2.执行构造方法,初始化对象
                    3.把对象指向内存空间,才能保证这个对象new完了
                    所以在多线程的情况下,可能会出现指令重排现象
                    就是原本 123 顺序,可事实确实 132 顺序,执行的顺序不一致

                    线程1:执行的顺序是 132
                    在线程1执行到3时,进来了一个线程2,线程2一看都执行到3了,
                    导致doubleCheckedLockingSingleton!=null,直接return doubleCheckedLockingSingleton;
                    但是其实此时这个对象还没有完成构造,那么这个对象就是虚无的

                    所以必须在 private static DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;
                    基础上,添加volatile
                     */
                    doubleCheckedLockingSingleton = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return doubleCheckedLockingSingleton;
    }

    // 多线程下测试
    public static void main(String[] args) {
        // 之前我是创建了对象,那么现在我根本就不去创建这个对象
        // DoubleCheckedLockingSingleton instance = DoubleCheckedLockingSingleton.getInstance();
        try {
            Field declaredFields = DoubleCheckedLockingSingleton.class.getDeclaredField("laity");
            declaredFields.setAccessible(true);

            Constructor<DoubleCheckedLockingSingleton> declaredConstructor = DoubleCheckedLockingSingleton.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            // 两个对象都是基于反射new出来的
            DoubleCheckedLockingSingleton doubleCheckedLockingSingleton1 = declaredConstructor.newInstance();
            declaredFields.set(doubleCheckedLockingSingleton1, false);
            DoubleCheckedLockingSingleton doubleCheckedLockingSingleton2 = declaredConstructor.newInstance();
            System.out.println(doubleCheckedLockingSingleton1 == doubleCheckedLockingSingleton2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

分析

哈哈哈,经历了这么多次破坏,这个万恶的反射怎么解决呢?

分析一波源码:

设计模式-单例设计模式详解_第1张图片

设计模式-单例设计模式详解_第2张图片

    /**
     * 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(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; }

重要

跟着我的节奏昂,来仔细分析一波

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance2 == instance1);  // true
    }
}

首先我们创建一个枚举类,并进行测试,发现是相等的;

接下来我们查看编译后的枚举类

设计模式-单例设计模式详解_第3张图片

接下来我们尝试通过反射来破坏单例

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.enums.EnumSingle
 * @Date: 2023年11月20日 23:40
 * @Description: 基于枚举解决反射的破坏
 * enum是一个什么?本身也是一个Class类
 */

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}


class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance2 == instance1);  // true

        // 尝试反射破坏枚举
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();

        // java.lang.NoSuchMethodException: com.laity.single.enums.EnumSingle.()
        // 没有空参构造器
        System.out.println(instance1);
        System.out.println(enumSingle);
    }
}

执行结果

D:\Java\JDK\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:61433,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2020.3\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\java\JDK\jre\lib\charsets.jar;D:\java\JDK\jre\lib\deploy.jar;D:\java\JDK\jre\lib\ext\access-bridge-64.jar;D:\java\JDK\jre\lib\ext\cldrdata.jar;D:\java\JDK\jre\lib\ext\dnsns.jar;D:\java\JDK\jre\lib\ext\jaccess.jar;D:\java\JDK\jre\lib\ext\jfxrt.jar;D:\java\JDK\jre\lib\ext\localedata.jar;D:\java\JDK\jre\lib\ext\mysql-connector-java-5.1.48.jar;D:\java\JDK\jre\lib\ext\nashorn.jar;D:\java\JDK\jre\lib\ext\sunec.jar;D:\java\JDK\jre\lib\ext\sunjce_provider.jar;D:\java\JDK\jre\lib\ext\sunmscapi.jar;D:\java\JDK\jre\lib\ext\sunpkcs11.jar;D:\java\JDK\jre\lib\ext\zipfs.jar;D:\java\JDK\jre\lib\javaws.jar;D:\java\JDK\jre\lib\jce.jar;D:\java\JDK\jre\lib\jfr.jar;D:\java\JDK\jre\lib\jfxswt.jar;D:\java\JDK\jre\lib\jsse.jar;D:\java\JDK\jre\lib\management-agent.jar;D:\java\JDK\jre\lib\plugin.jar;D:\java\JDK\jre\lib\resources.jar;D:\java\JDK\jre\lib\rt.jar;D:\LaityWork\architecture\foodie-dev\target\classes;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter\2.1.5.RELEASE\spring-boot-starter-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot\2.1.5.RELEASE\spring-boot-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-context\5.1.7.RELEASE\spring-context-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-autoconfigure\2.1.5.RELEASE\spring-boot-autoconfigure-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-core\5.1.7.RELEASE\spring-core-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-jcl\5.1.7.RELEASE\spring-jcl-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-web\2.1.5.RELEASE\spring-boot-starter-web-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-json\2.1.5.RELEASE\spring-boot-starter-json-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-databind\2.9.8\jackson-databind-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-core\2.9.8\jackson-core-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.8\jackson-datatype-jdk8-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.8\jackson-datatype-jsr310-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.8\jackson-module-parameter-names-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-tomcat\2.1.5.RELEASE\spring-boot-starter-tomcat-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-core\9.0.19\tomcat-embed-core-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-el\9.0.19\tomcat-embed-el-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.19\tomcat-embed-websocket-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\hibernate\validator\hibernate-validator\6.0.16.Final\hibernate-validator-6.0.16.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-web\5.1.7.RELEASE\spring-web-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-beans\5.1.7.RELEASE\spring-beans-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-webmvc\5.1.7.RELEASE\spring-webmvc-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-expression\5.1.7.RELEASE\spring-expression-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-aop\2.1.5.RELEASE\spring-boot-starter-aop-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-aop\5.1.7.RELEASE\spring-aop-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\aspectj\aspectjweaver\1.9.4\aspectjweaver-1.9.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-configuration-processor\2.1.5.RELEASE\spring-boot-configuration-processor-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\spring\boot\mybatis-spring-boot-starter\2.1.0\mybatis-spring-boot-starter-2.1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-jdbc\2.1.5.RELEASE\spring-boot-starter-jdbc-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-jdbc\5.1.7.RELEASE\spring-jdbc-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-tx\5.1.7.RELEASE\spring-tx-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\spring\boot\mybatis-spring-boot-autoconfigure\2.1.0\mybatis-spring-boot-autoconfigure-2.1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\mybatis-spring\2.0.2\mybatis-spring-2.0.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring-boot-starter\2.1.5\mapper-spring-boot-starter-2.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-core\1.1.5\mapper-core-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-base\1.1.5\mapper-base-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-weekend\1.1.5\mapper-weekend-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring\1.1.5\mapper-spring-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-extra\1.1.5\mapper-extra-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring-boot-autoconfigure\2.1.5\mapper-spring-boot-autoconfigure-2.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper-spring-boot-starter\1.2.12\pagehelper-spring-boot-starter-1.2.12.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper-spring-boot-autoconfigure\1.2.12\pagehelper-spring-boot-autoconfigure-1.2.12.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper\5.1.10\pagehelper-5.1.10.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\jsqlparser\jsqlparser\2.0\jsqlparser-2.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\commons\commons-lang3\3.4\commons-lang3-3.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\commons-io\commons-io\2.4\commons-io-2.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger2\2.4.0\springfox-swagger2-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\swagger\swagger-annotations\1.5.6\swagger-annotations-1.5.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\swagger\swagger-models\1.5.6\swagger-models-1.5.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-spi\2.4.0\springfox-spi-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-core\2.4.0\springfox-core-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-schema\2.4.0\springfox-schema-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger-common\2.4.0\springfox-swagger-common-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-spring-web\2.4.0\springfox-spring-web-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\google\guava\guava\18.0\guava-18.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger-ui\2.4.0\springfox-swagger-ui-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\xiaoymin\swagger-bootstrap-ui\1.6\swagger-bootstrap-ui-1.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\slf4j\slf4j-log4j12\1.7.21\slf4j-log4j12-1.7.21.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\log4j\log4j\1.2.17\log4j-1.2.17.jar;E:\IdeaInstall\IntelliJ IDEA 2020.3.4\lib\idea_rt.jar" com.laity.single.enums.Test
Connected to the target VM, address: '127.0.0.1:61433', transport: 'socket'
true
Exception in thread "main" java.lang.NoSuchMethodException: com.laity.single.enums.EnumSingle.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at com.laity.single.enums.Test.main(EnumSingle.java:30)
Disconnected from the target VM, address: '127.0.0.1:61433', transport: 'socket'

Process finished with exit code 1

java.lang.NoSuchMethodException: com.laity.single.enums.EnumSingle.()

说:没有找到空参构造器

编译后的代码中写着有空参构造器,但是我们通过Java程序运行发现根本没有这个玩应,我透这IDEA欺骗了我,这不是坑爹吗?

反编译一波看看这个源代码究竟如何,跟好节奏昂

设计模式-单例设计模式详解_第4张图片

设计模式-单例设计模式详解_第5张图片

好好好,逼我用大招

设计模式-单例设计模式详解_第6张图片

我要拿出我的jad来看看究竟是何方妖孽

下载地址:https://varaneckas.com/jad/

设计模式-单例设计模式详解_第7张图片

生成的java文件

// 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:   EnumSingle.java

package com.laity.single.enums;


public final class EnumSingle extends Enum
{

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

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/laity/single/enums/EnumSingle, name);
    }

    // 这里可以看到 不是无参构造器了,是有参构造器
    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

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

那么我们继续测试

package com.laity.single.enums;

import java.lang.reflect.Constructor;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.enums.EnumSingle
 * @Date: 2023年11月20日 23:40
 * @Description: 基于枚举解决反射的破坏
 * enum是一个什么?本身也是一个Class类
 */

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}


class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance2 == instance1);  // true

        // 尝试反射破坏枚举
        // private EnumSingle(String s, int i) 通过编译后可以看到参数
        // 这里需要注意 一定不要写成Integer
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();

        // java.lang.NoSuchMethodException: com.laity.single.enums.EnumSingle.()
        // 没有空参构造器
        System.out.println(instance1);
        System.out.println(enumSingle);
    }
}

结果 // Cannot reflectively create enum objects

D:\Java\JDK\bin\java.exe "-javaagent:E:\IdeaInstall\IntelliJ IDEA 2020.3.4\lib\idea_rt.jar=50406:E:\IdeaInstall\IntelliJ IDEA 2020.3.4\bin" -Dfile.encoding=UTF-8 -classpath D:\java\JDK\jre\lib\charsets.jar;D:\java\JDK\jre\lib\deploy.jar;D:\java\JDK\jre\lib\ext\access-bridge-64.jar;D:\java\JDK\jre\lib\ext\cldrdata.jar;D:\java\JDK\jre\lib\ext\dnsns.jar;D:\java\JDK\jre\lib\ext\jaccess.jar;D:\java\JDK\jre\lib\ext\jfxrt.jar;D:\java\JDK\jre\lib\ext\localedata.jar;D:\java\JDK\jre\lib\ext\mysql-connector-java-5.1.48.jar;D:\java\JDK\jre\lib\ext\nashorn.jar;D:\java\JDK\jre\lib\ext\sunec.jar;D:\java\JDK\jre\lib\ext\sunjce_provider.jar;D:\java\JDK\jre\lib\ext\sunmscapi.jar;D:\java\JDK\jre\lib\ext\sunpkcs11.jar;D:\java\JDK\jre\lib\ext\zipfs.jar;D:\java\JDK\jre\lib\javaws.jar;D:\java\JDK\jre\lib\jce.jar;D:\java\JDK\jre\lib\jfr.jar;D:\java\JDK\jre\lib\jfxswt.jar;D:\java\JDK\jre\lib\jsse.jar;D:\java\JDK\jre\lib\management-agent.jar;D:\java\JDK\jre\lib\plugin.jar;D:\java\JDK\jre\lib\resources.jar;D:\java\JDK\jre\lib\rt.jar;D:\LaityWork\architecture\foodie-dev\target\classes;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter\2.1.5.RELEASE\spring-boot-starter-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot\2.1.5.RELEASE\spring-boot-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-context\5.1.7.RELEASE\spring-context-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-autoconfigure\2.1.5.RELEASE\spring-boot-autoconfigure-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-core\5.1.7.RELEASE\spring-core-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-jcl\5.1.7.RELEASE\spring-jcl-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-web\2.1.5.RELEASE\spring-boot-starter-web-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-json\2.1.5.RELEASE\spring-boot-starter-json-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-databind\2.9.8\jackson-databind-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-core\2.9.8\jackson-core-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.8\jackson-datatype-jdk8-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.8\jackson-datatype-jsr310-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.8\jackson-module-parameter-names-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-tomcat\2.1.5.RELEASE\spring-boot-starter-tomcat-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-core\9.0.19\tomcat-embed-core-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-el\9.0.19\tomcat-embed-el-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.19\tomcat-embed-websocket-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\hibernate\validator\hibernate-validator\6.0.16.Final\hibernate-validator-6.0.16.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-web\5.1.7.RELEASE\spring-web-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-beans\5.1.7.RELEASE\spring-beans-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-webmvc\5.1.7.RELEASE\spring-webmvc-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-expression\5.1.7.RELEASE\spring-expression-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-aop\2.1.5.RELEASE\spring-boot-starter-aop-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-aop\5.1.7.RELEASE\spring-aop-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\aspectj\aspectjweaver\1.9.4\aspectjweaver-1.9.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-configuration-processor\2.1.5.RELEASE\spring-boot-configuration-processor-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\spring\boot\mybatis-spring-boot-starter\2.1.0\mybatis-spring-boot-starter-2.1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-jdbc\2.1.5.RELEASE\spring-boot-starter-jdbc-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-jdbc\5.1.7.RELEASE\spring-jdbc-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-tx\5.1.7.RELEASE\spring-tx-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\spring\boot\mybatis-spring-boot-autoconfigure\2.1.0\mybatis-spring-boot-autoconfigure-2.1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\mybatis-spring\2.0.2\mybatis-spring-2.0.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring-boot-starter\2.1.5\mapper-spring-boot-starter-2.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-core\1.1.5\mapper-core-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-base\1.1.5\mapper-base-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-weekend\1.1.5\mapper-weekend-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring\1.1.5\mapper-spring-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-extra\1.1.5\mapper-extra-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring-boot-autoconfigure\2.1.5\mapper-spring-boot-autoconfigure-2.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper-spring-boot-starter\1.2.12\pagehelper-spring-boot-starter-1.2.12.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper-spring-boot-autoconfigure\1.2.12\pagehelper-spring-boot-autoconfigure-1.2.12.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper\5.1.10\pagehelper-5.1.10.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\jsqlparser\jsqlparser\2.0\jsqlparser-2.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\commons\commons-lang3\3.4\commons-lang3-3.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\commons-io\commons-io\2.4\commons-io-2.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger2\2.4.0\springfox-swagger2-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\swagger\swagger-annotations\1.5.6\swagger-annotations-1.5.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\swagger\swagger-models\1.5.6\swagger-models-1.5.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-spi\2.4.0\springfox-spi-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-core\2.4.0\springfox-core-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-schema\2.4.0\springfox-schema-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger-common\2.4.0\springfox-swagger-common-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-spring-web\2.4.0\springfox-spring-web-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\google\guava\guava\18.0\guava-18.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger-ui\2.4.0\springfox-swagger-ui-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\xiaoymin\swagger-bootstrap-ui\1.6\swagger-bootstrap-ui-1.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\slf4j\slf4j-log4j12\1.7.21\slf4j-log4j12-1.7.21.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\log4j\log4j\1.2.17\log4j-1.2.17.jar com.laity.single.enums.Test
true
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.laity.single.enums.Test.main(EnumSingle.java:34)

Process finished with exit code 1

这就对应上我们之前看的newInstance()中的源码了,所以可以得出结论反射确实不能破坏枚举的单例。

解决

基于枚举

package com.laity.single.enums;

import java.lang.reflect.Constructor;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.enums.EnumSingle
 * @Date: 2023年11月20日 23:40
 * @Description: 基于枚举解决反射的破坏
 * enum是一个什么?本身也是一个Class类
 */

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}


class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance2 == instance1);  // true

        // 尝试反射破坏枚举
        // private EnumSingle(String s, int i) 通过编译后可以看到参数
        // 这里需要注意 一定不要写成Integer
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();

        // java.lang.NoSuchMethodException: com.laity.single.enums.EnumSingle.()
        // 没有空参构造器
        System.out.println(instance1);
        System.out.println(enumSingle);
    }
}
D:\Java\JDK\bin\java.exe "-javaagent:E:\IdeaInstall\IntelliJ IDEA 2020.3.4\lib\idea_rt.jar=50406:E:\IdeaInstall\IntelliJ IDEA 2020.3.4\bin" -Dfile.encoding=UTF-8 -classpath D:\java\JDK\jre\lib\charsets.jar;D:\java\JDK\jre\lib\deploy.jar;D:\java\JDK\jre\lib\ext\access-bridge-64.jar;D:\java\JDK\jre\lib\ext\cldrdata.jar;D:\java\JDK\jre\lib\ext\dnsns.jar;D:\java\JDK\jre\lib\ext\jaccess.jar;D:\java\JDK\jre\lib\ext\jfxrt.jar;D:\java\JDK\jre\lib\ext\localedata.jar;D:\java\JDK\jre\lib\ext\mysql-connector-java-5.1.48.jar;D:\java\JDK\jre\lib\ext\nashorn.jar;D:\java\JDK\jre\lib\ext\sunec.jar;D:\java\JDK\jre\lib\ext\sunjce_provider.jar;D:\java\JDK\jre\lib\ext\sunmscapi.jar;D:\java\JDK\jre\lib\ext\sunpkcs11.jar;D:\java\JDK\jre\lib\ext\zipfs.jar;D:\java\JDK\jre\lib\javaws.jar;D:\java\JDK\jre\lib\jce.jar;D:\java\JDK\jre\lib\jfr.jar;D:\java\JDK\jre\lib\jfxswt.jar;D:\java\JDK\jre\lib\jsse.jar;D:\java\JDK\jre\lib\management-agent.jar;D:\java\JDK\jre\lib\plugin.jar;D:\java\JDK\jre\lib\resources.jar;D:\java\JDK\jre\lib\rt.jar;D:\LaityWork\architecture\foodie-dev\target\classes;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter\2.1.5.RELEASE\spring-boot-starter-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot\2.1.5.RELEASE\spring-boot-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-context\5.1.7.RELEASE\spring-context-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-autoconfigure\2.1.5.RELEASE\spring-boot-autoconfigure-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-core\5.1.7.RELEASE\spring-core-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-jcl\5.1.7.RELEASE\spring-jcl-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-web\2.1.5.RELEASE\spring-boot-starter-web-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-json\2.1.5.RELEASE\spring-boot-starter-json-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-databind\2.9.8\jackson-databind-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-core\2.9.8\jackson-core-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.8\jackson-datatype-jdk8-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.8\jackson-datatype-jsr310-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.8\jackson-module-parameter-names-2.9.8.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-tomcat\2.1.5.RELEASE\spring-boot-starter-tomcat-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-core\9.0.19\tomcat-embed-core-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-el\9.0.19\tomcat-embed-el-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.19\tomcat-embed-websocket-9.0.19.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\hibernate\validator\hibernate-validator\6.0.16.Final\hibernate-validator-6.0.16.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-web\5.1.7.RELEASE\spring-web-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-beans\5.1.7.RELEASE\spring-beans-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-webmvc\5.1.7.RELEASE\spring-webmvc-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-expression\5.1.7.RELEASE\spring-expression-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-aop\2.1.5.RELEASE\spring-boot-starter-aop-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-aop\5.1.7.RELEASE\spring-aop-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\aspectj\aspectjweaver\1.9.4\aspectjweaver-1.9.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-configuration-processor\2.1.5.RELEASE\spring-boot-configuration-processor-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\spring\boot\mybatis-spring-boot-starter\2.1.0\mybatis-spring-boot-starter-2.1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\boot\spring-boot-starter-jdbc\2.1.5.RELEASE\spring-boot-starter-jdbc-2.1.5.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-jdbc\5.1.7.RELEASE\spring-jdbc-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\spring-tx\5.1.7.RELEASE\spring-tx-5.1.7.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\spring\boot\mybatis-spring-boot-autoconfigure\2.1.0\mybatis-spring-boot-autoconfigure-2.1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\mybatis\mybatis-spring\2.0.2\mybatis-spring-2.0.2.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring-boot-starter\2.1.5\mapper-spring-boot-starter-2.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-core\1.1.5\mapper-core-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-base\1.1.5\mapper-base-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-weekend\1.1.5\mapper-weekend-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring\1.1.5\mapper-spring-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-extra\1.1.5\mapper-extra-1.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\tk\mybatis\mapper-spring-boot-autoconfigure\2.1.5\mapper-spring-boot-autoconfigure-2.1.5.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper-spring-boot-starter\1.2.12\pagehelper-spring-boot-starter-1.2.12.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper-spring-boot-autoconfigure\1.2.12\pagehelper-spring-boot-autoconfigure-1.2.12.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\pagehelper\pagehelper\5.1.10\pagehelper-5.1.10.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\jsqlparser\jsqlparser\2.0\jsqlparser-2.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\apache\commons\commons-lang3\3.4\commons-lang3-3.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\commons-io\commons-io\2.4\commons-io-2.4.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger2\2.4.0\springfox-swagger2-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\swagger\swagger-annotations\1.5.6\swagger-annotations-1.5.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\swagger\swagger-models\1.5.6\swagger-models-1.5.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-spi\2.4.0\springfox-spi-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-core\2.4.0\springfox-core-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-schema\2.4.0\springfox-schema-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger-common\2.4.0\springfox-swagger-common-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-spring-web\2.4.0\springfox-spring-web-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\google\guava\guava\18.0\guava-18.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\io\springfox\springfox-swagger-ui\2.4.0\springfox-swagger-ui-2.4.0.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\com\github\xiaoymin\swagger-bootstrap-ui\1.6\swagger-bootstrap-ui-1.6.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\org\slf4j\slf4j-log4j12\1.7.21\slf4j-log4j12-1.7.21.jar;D:\java\Maven\apache-maven-3.6.3\mvn_resp\log4j\log4j\1.2.17\log4j-1.2.17.jar com.laity.single.enums.Test
true
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.laity.single.enums.Test.main(EnumSingle.java:34)

Process finished with exit code 1

直接结论

道高一尺魔高一丈

私有构造函数,防止外部直接实例化

在面向对象编程中,类的构造函数用于创建该类的实例。通过声明构造函数为私有,就意味着该类的实例化只能在类的内部进行,而不能在外部直接通过 new 关键字实例化。

这样的设计有几个常见的用途:

  • 控制实例化过程: 通过私有构造函数,可以在构造函数中进行一些额外的逻辑或者初始化操作,以确保对象被正确地创建。这也可以防止类的实例化过程变得复杂或者出现错误。
  • 单例模式: 单例模式是一种常见的设计模式,其中类的实例被限制为一个。通过将构造函数设为私有,可以防止在外部创建多个实例。
  • 工具类: 有些类只包含静态方法,不需要实例化。将构造函数设为私有可以确保不会意外地创建类的实例。
  • 封装实现细节: 通过将构造函数设为私有,类可以隐藏其实现细节,只允许通过提供的接口来与外部进行交互。这有助于实现封装和信息隐藏的概念。

下面是一个示例,演示了如何通过私有构造函数防止外部直接实例化:

public class MyClass {
    private String data;

    // 私有构造函数
    private MyClass(String data) {
        this.data = data;
    }

    // 公有静态工厂方法,用于创建实例
    public static MyClass createInstance(String data) {
        return new MyClass(data);
    }

    // 其他公有方法
    public String getData() {
        return data;
    }
}

在这个例子中,MyClass 类的构造函数是私有的,所以不能在类的外部直接通过 new MyClass(…) 进行实例化。相反,类提供了一个公有的静态工厂方法 createInstance,通过这个方法来创建类的实例。这样设计可以让类对实例化过程进行更多的控制和逻辑处理。

最慢的步伐不是跬步,而是徘徊;最快的脚步不是冲刺,而是坚持。我是Laity,正在前行的Laity。

你可能感兴趣的:(Spring相关技术应用,设计模式,单例模式)