生命无罪,健康万岁,我是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();
}
}
}
分析
哈哈哈,经历了这么多次破坏,这个万恶的反射怎么解决呢?
分析一波源码:
/**
* 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™ 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
}
}
首先我们创建一个枚举类,并进行测试,发现是相等的;
接下来我们查看编译后的枚举类
接下来我们尝试通过反射来破坏单例
/**
* @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欺骗了我,这不是坑爹吗?
反编译一波看看这个源代码究竟如何,跟好节奏昂
好好好,逼我用大招
我要拿出我的jad来看看究竟是何方妖孽
下载地址:https://varaneckas.com/jad/
生成的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。