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

生命无罪,健康万岁,我是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();
        }
    }
}

分析

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

分析一波源码:

img

img

    /**
     * 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.
     *
     * <p>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.
     *
     * <p>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
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * <p>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.
     *
     * <p>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
    }
}

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

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

img

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

/**
 * @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.<init>()
        // 没有空参构造器
        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欺骗了我,这不是坑爹吗?

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

img

img

好好好,逼我用大招

img

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

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

img

生成的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.<init>()
        // 没有空参构造器
        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.<init>()
        // 没有空参构造器
        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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/254583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在Windows系统上部署VisualSVN并实现远程访问管理界面【内网穿透】

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统…

如何将文档秒变仿真翻页电子书

文档太多&#xff0c;存放起来又乱的朋友们&#xff0c;可以看看这个方法能不能帮助你。将文档里的图片或是PPT、Word等一键转换成仿真翻页的电子书。既不占内存&#xff0c;客户也能方便观看各种信息等。 先看看我制作的仿真翻页电子书的效果吧&#xff01; 怎么样&#xff1…

七、 Shell 变量名规范

在 Shell 编程中&#xff0c;变量是用于存储和引用数据值的名称。 定义变量时&#xff0c;变量名不需要添加美元符号&#xff08;$&#xff09;&#xff0c;如&#xff1a; my_name"chaoqing" 注意&#xff0c;变量名和等号之间不能有空格&#xff0c;同时需要遵守…

vue3 使用 element-plus 侧边菜单栏多开收起时 出现 迟滞、卡顿的问题

遇到问题 vue3 使用 element-plus 侧边菜单栏多开收起时 出现 迟滞、卡顿的问题 这里多开二级&#xff0c;当点击上面的回收时&#xff0c;出现了卡顿现象&#xff0c;找了很久才发现是因为引入了icon图标 具体原因不详&#xff01;&#xff01;&#xff01;&#xff01;坑啊 …

XUbuntu22.04之Wx公众号排版工具mdx-editor(二百)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

无惧数据泄露风险:迅软科技与电子科技公司共同谱写的安全合奏

当今&#xff0c;电子信息产品已广泛渗透到我们生活的各个领域&#xff0c;涵盖通信、医疗、计算机及其周边视听产品、玩具、军工等多个领域。电子信息科技行业作为典型的知识技术密集型行业&#xff0c;具备科技含量高、专利众多、知识产权丰富、核心数据密级高等特点。随着行…

【AI美图提示词】第07期效果图,AI人工智能自动绘画,精选绝美版美图欣赏

AI诗配画 山水画中景如画&#xff0c;云雾缭绕峰峦间。桥畔流水潺潺响&#xff0c;诗意盎然山水间。上面的诗句和图片全部来自AI自动化完成&#xff0c;这就是技术的力量&#xff0c;接下来我们进行模型生成学习&#xff1a; 先上原始底图&#xff1a; 下面是模型生成效果图&a…

Spring Boot自动装配原理以及实践

了解自动装配两个核心 Import注解的作用 Import说Spring框架经常会看到的注解&#xff0c;它有以下几个作用: 导入Configuration类下所有的bean方法中创建的bean。导入import指定的bean&#xff0c;例如Import(AService.class)&#xff0c;就会生成AService的bean&#xff0…

如何实现公网访问本地内网搭建的WBO白板远程协作办公【内网穿透】

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 前言1. 部署WBO白板2. 本地访问WBO白板3. Linux 安装cp…

mipi屏幕的供电和背光ledk

目录 屏幕供电 背光供电 屏幕供电 mipi的高通供电主要是2.8V和1.8V两个屏幕供电 author daisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 https://blog.csdn.net/qq_40715266?typeblog 供电配置路径 kernel/msm-3.18/arch/arm/boot/dts/P322/msm8953-mdss-panels.dtsi …

Kubernetes - Dashboard Token 访问登录永不过期配置

如图查看 1 2 3 4 步骤 选择 dashboard 的命名空间&#xff0c;有些版本估计在 kube-system&#xff0c;具体情况具体分析选择 Deployments找到对应 kubernetes-dashboard编辑 yml 添加配置项 - --token-ttl0 需要重新大刷下&#xff0c;重新登录下即可~

用Python完成下列问题。给你一个非空整数列表,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

用Python完成下列问题。给你一个非空整数列表&#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 示例 1 &#xff1a; 输入&#xff1a;nums [2,2,1]输出&#xff1a;1 示例 2 &#xff1a; 输入&#xff1a;nums [4…

Vue3创建应用教程

前提条件&#xff1a; 熟悉命令行已安装 16.0 或更高版本的 Node.js 命令行 在当前工作目录输入cmd打开命令行窗口在命令行窗口输入(不需要输入>号&#xff09; 这一指令将会安装并执行 create-vue&#xff0c;它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScr…

PageView组件实现翻页和自定义轮播图

PageView实现上下翻页 import package:flutter/material.dart; import package:flutter/services.dart; import package:flutter_demo/tools/pageViewKeepAlive.dart;void main() {//设置状态栏颜色SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(statusBar…

实现Springboot的自动装配

首先我们有一个模块叫testOne 该模块就只定义一个类UserService。 我们要将UserService自动装配到Springboot中。 一下是testOne的各个文件。 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM…

LinuxCNC的使用

先进行程序设置 点击“开始”,选择“创建新的配置” 设置好机床名称和单位 关键是需要设置并口地址 查看并口使用命令:lscpi -v 将使用的并口填入: 这里是设置页面

Cesium 展示——实现球体和背景分离

文章目录 需求分析补充:添加监听让Cesium随窗口大小自适应需求 页面本身body有一个背景,想要只加载cesium球体,而背景设为透明,露出body的背景,有没有方法实现呢 注意:图片是随机找的一张 分析 html<!-- Cesium --> <divid="cesiumContainer"style…

MySQL通过Binlog日志恢复数据库

一、MySQL Binlog 简介 MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志&#xff0c;它记录了所有的 DDL 和 DML 语句&#xff08;除了数据查询语句select、show等&#xff09;&#xff0c;以事件形式记录&#xff0c;还包含语句所执行的消耗的时间&#xff0c;MySQL的…

onnx 图像分类

参考文章&#xff1a; 【netron】模型可视化工具netron-CSDN博客 Pytorch图像分类模型部署-ONNX Runtime本地终端推理_哔哩哔哩_bilibili 使用netron可视化模型结构 1&#xff09;使用在线版 浏览器访问&#xff1a;Netron 点击 “Open Model” 按钮&#xff0c;选择要可…

数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog

《数据库灾难应对&#xff1a;MySQL误删除数据的救赎之道&#xff0c;技巧get起来&#xff01;之binlog》 数据意外删除是数据库管理中常见的问题之一。MySQL作为广泛使用的数据库管理系统&#xff0c;当数据意外删除时&#xff0c;有几种方法可以尝试恢复数据。以下是binlog方…