通常我们所使用的单例模式,我们都可以使用反射使它不再单例,如下饿汉式的单例模式:
public final class Singleton {
private static final Singleton instance=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
测试案例如下:
Singleton singleton1=Singleton.getInstance();
Singleton singleton2=Singleton.getInstance();
Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton3=constructor.newInstance();
System.out.println(singleton1);
System.out.println(singleton2);
System.out.println(singleton3);
System.out.println(singleton1==singleton2);
System.out.println(singleton1==singleton3);
其中singleton1、singleton2都是通过我们所实现的单例模式来获取的对象,他们应该是同一个对象,singleton3则是通过反射获取无参构造器,constructor.setAccessible(true)来获取访问权限,最后通过无参构造器来创建一个对象singleton3,singleton3和上述两者应该不是同一个对象,测试结果如下:
com.lg.design.singleton.hungry.Singleton@15e3d24a
com.lg.design.singleton.hungry.Singleton@15e3d24a
com.lg.design.singleton.hungry.Singleton@20030380
true
false
所以说通常我们所使用的单例模式,我们都可以使用反射使它不再单例。然而单例使用枚举的话,却可以避免被反射。
单例如下:
public enum Singleton {
instance;
private Singleton(){}
}
反射如下:
Singleton singleton1=Singleton.instance;
Singleton singleton2=Singleton.instance;
Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton3=constructor.newInstance();
System.out.println(singleton1);
System.out.println(singleton2);
System.out.println(singleton3);
System.out.println(singleton1==singleton2);
System.out.println(singleton1==singleton3);
然后就报错:
Exception in thread "main" java.lang.NoSuchMethodException: com.lg.design.singleton.enumsingleton.Singleton.<init>()
at java.lang.Class.getConstructor0(Class.java:2849)
at java.lang.Class.getDeclaredConstructor(Class.java:2053)
at com.lg.design.singleton.enumsingleton.Test.main(Test.java:14)
没有这个无参构造器,通过调试Singleton.class.getDeclaredConstructors()获取所有构造器,会发现并没有我们所设置的无参构造器,只有一个参数为(String.class,int.class)构造器,然后我们就可以明白了,这里的参数其实就是枚举的名字和所在枚举中位置,即枚举的name和ordinal两个属性,枚举的源码如下:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
private final String name;
public final String name() {
return name;
}
private final int ordinal;
public final int ordinal() {
return ordinal;
}
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
//略
}
枚举Enum是一个抽象类,一个类一旦声明为枚举,其实就是继承了Enum,所以会有(String.class,int.class)的构造器(就是父类Enum的构造器),具体的原理可以根据生成的字节码反编译后得知,可以参考这篇文章http://pf-miles.iteye.com/blog/187155#bc2340028。
既然是可以获取到父类Enum的构造器,那我们就使用该构造器看能不能创建出对象:
Singleton singleton1=Singleton.instance;
Singleton singleton2=Singleton.instance;
Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor(String.class,int.class);
//Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton3=constructor.newInstance("otherInstance",9);
//Singleton singleton3=constructor.newInstance();
System.out.println(singleton1);
System.out.println(singleton2);
System.out.println(singleton3);
System.out.println(singleton1==singleton2);
System.out.println(singleton1==singleton3);
然后也报错:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:521)
at com.lg.design.singleton.enumsingleton.Test.main(Test.java:16)
之前的错是说没有构造器,这次我们能够拿到构造器了,只是在使用构造器执行newInstance("otherInstance",9)方法时抛出异常,说不能够反射枚举,具体源码如下:
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);
}
}
//我们关注的重点,如果类含有ENUM修饰,调用该方法时直接报错
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
return (T) ca.newInstance(initargs);
}
也就是说反射在通过newInstance创建对象时,会检查该类是否是枚举类,如果是,则抛出异常,反射失败。
也就是说使用枚举可以避免被反射,从而可以达到单例的效果。
若想转载请注明出处: http://lgbolgger.iteye.com/blog/2159940
作者:iteye的乒乓狂魔
分享到:
相关推荐
JAVA 枚举单例模式是一种特殊的单例模式实现方式,它使用枚举类型来保证线程安全、防止序列化问题和反射攻击。下面我们将详细解释这个模式的实现原理和源码分析。 线程安全 在 Java 中,枚举类型的实例是在类加载...
以下是一个简单的枚举单例模式的示例: ```java public enum Singleton { INSTANCE; public void someService() { // 实现服务逻辑 } } ``` 在这个例子中,Singleton是一个枚举类,它有一个名为INSTANCE的...
xml、单例模式、反射、枚举.
本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)...进阶篇有反射、泛型、注解、网络编程、多线程、序列化、数据库、Servlet、JSP、XML解析、单例模式与枚举。本专栏主要为Java入门者提供实验参考。
5. 枚举单例:最安全且避免了序列化问题,但可能不适合需要扩展的情况。 编写单例模式需要注意以下问题: 1. 避免多线程环境下的并发问题,确保实例的唯一性。 2. 考虑序列化可能导致的多个实例,需要在类中添加`...
本文将详细讨论四种常见的单例实现方式:饿汉模式、懒汉模式、双重检查锁定(DCL)单例模式以及枚举单例。 1. **饿汉模式**: 饿汉模式是在类加载时就完成了实例化,避免了线程同步问题。这种方式简单且安全,但...
《Effective Java》和《Java 与模式》等书籍推荐使用枚举实现单例,因为它们提供了一种天然的防篡改机制,保证了在任何情况下都只有一个实例。 5. **枚举的优势**: - **安全性**:枚举对象是不可变的,防止了意外...
6. 枚举型单例: 最安全的实现方式,天然线程安全,防止反射攻击。 ```java public enum Singleton { INSTANCE; } ``` 现在,我们将创建一个工具类`SingletonFactory`来封装这些单例模式的实现。`SingletonFactory...
枚举的单例在Java中被认为是最好的实现方式,因为它不仅线程安全,而且防止了序列化和反射攻击。 每种实现方式都有其特点和适用场景,开发者应根据实际需求选择合适的单例实现。在实际项目中,还要考虑性能、线程...
其中,枚举式单例模式是最安全的实现方式,因为它可以避免反射漏洞和反序列化漏洞。其他四种实现方式都存在反射漏洞和反序列化漏洞,需要采取相应的防护措施来避免这些漏洞。 在实际开发中,需要根据实际情况选择...
枚举单例写法简洁,能够避免反射破坏单例的问题,并且它还能够防止反序列化重新创建新的实例。使用枚举实现单例模式时,Java虚拟机会保证枚举类型中的字段只被实例化一次。 在Java的单例模式实现中,内部类也是一个...
4.枚举单例是 Java 中推荐的实现方式,它天然线程安全,无需额外的同步: ```java public enum SingletonClass { INSTANCE; public void singletonMethod() { // ... } } ``` 枚举的单例在防止反射攻击方面也...
如果希望防止单例被恶意反射创建,枚举单例是最佳选择。 在使用单例模式时,需要注意以下几点: 1. 避免过早初始化:懒汉式可以延迟初始化,减少不必要的资源占用。 2. 确保线程安全:在多线程环境下,需要确保单例...
在Java编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在需要频繁创建和销毁对象的场景中尤其...而在枚举单例中,由于其简洁性和安全性,通常被视为最佳实践。
枚举的单例模式简单易懂,且天然线程安全,不会受到反射和序列化攻击的影响,是推荐的单例实现方式之一。 总的来说,单例模式是一种常见的设计模式,懒汉式单例模式则是其中一种实现策略,它的主要特点是延迟加载和...
除了上述的单例模式实现,Java中还提供了`Enum`方式来创建单例,这种方式不仅简单且线程安全,同时也避免了反射和序列化攻击。例如: ```java public enum Singleton { INSTANCE; } ``` 通过枚举方式实现单例,...
5. **枚举单例**: 将单例定义为一个枚举类型,这样不仅能保证线程安全,还能防止反射攻击。枚举是JVM的固有特性,因此它的创建是线程安全的,并且不允许实例化多个枚举实例。 每种实现方式都有其优缺点,选择哪种...
静态内部类单例利用Java类加载机制保证了线程安全,而枚举单例则是Java中实现单例的最佳方式,因为它天然支持序列化且防止反射攻击。 在代码实现上,我们可以创建一个名为SingletonFactory的工厂类,其中包含一个...
枚举的单例在JVM层面就保证了唯一性,无需额外同步操作。 总结起来,单例模式的实现方式各有优缺点,需要根据实际应用场景选择合适的方式。需要注意的是,过度使用单例可能导致设计上的问题,如违反单一职责原则,...
在Java中,单例模式可以通过多种方式实现,如懒汉模式、饿汉模式、双重检查锁模式、枚举模式等。下面是一个简单的懒汉模式实现单例模式的示例代码: ```java public class Singleton { private static Singleton ...