本文介绍了如何使用反射机制破坏传统的单例类,以及如何使用Java的枚举类创造出最简捷,最安全的单例。
反射的功能非常强大,普通的单例,将默认的无参构造方法私有化,但是利用反射机制任然可以实现对私有构造器的访问,实例化出多个对象。
1. 新建一个懒汉模式的单例类 SingleTonUtils.java
/**
*
*/
package com.jnk.think.java.chapter.reflect;
/**
* 单例计数器
*
* @author NonkeyJiang
*
*/
public class SingleTonUtils
{
// 私有属性
private int timesCount;
// 私有化无参构造器
private SingleTonUtils()
{
timesCount = 0;
}
// 静态对象,所有用户公用一个对象
private static SingleTonUtils singleTonUtils;
// 获取静态对象的方法
public static SingleTonUtils getInstance()
{
if (null == singleTonUtils)
{
singleTonUtils = new SingleTonUtils();
}
return singleTonUtils;
}
}
2. 使用反射机制获取多个单例类的对象
private static void singleTonTest()
{
try
{
Class<?> obj = Class.forName("com.jnk.think.java.chapter.reflect.SingleTonUtils");// 获取Class对象
/*
* 获取无参构造器,带 Declared 的方法可以获取私有方法和属性,不带 Declared 的方法只能获取公有方法和属性
*/
Constructor<?> constructor = obj.getDeclaredConstructor();
constructor.setAccessible(true);// 将私有构造器的访问模式改为可以访问
Object newInstance = constructor.newInstance();// 使用构造器创建一个新对象
System.out.println(newInstance);// 打印对象的地址
newInstance = constructor.newInstance();// 使用构造器创建另一个新对象
System.out.println(newInstance);// 打印对象的地址
/**
* 两次打印的对象地址不一样,证明已经存在两个不同的对象,而这个类时单例的,说明这个单例已经失效了
*/
} catch (Exception e)
{
e.printStackTrace();
}
}
com.jnk.think.java.chapter.reflect.SingleTonUtils@15db9742
com.jnk.think.java.chapter.reflect.SingleTonUtils@6d06d69c
两次打印的对象地址不一样,证明已经存在两个不同的对象,而这个类时单例的,说明这个单例已经失效了
从Java 1.5开始实现单例可以使用一个单元素的枚举实现。
/**
*
*/
package com.jnk.think.java.chapter.reflect;
/**
* @author NonkeyJiang
*
*/
public enum EnumSingleTon
{
TIMES(0);
private int times;
private EnumSingleTon(int times)
{
this.times = times;
}
public void increment()
{
times++;
}
public int getTimes()
{
return times;
}
private void p()
{
}
}
public static void enumSingleTonTest()
{
try
{
Class<?> obj = Class.forName("com.jnk.think.java.chapter.reflect.EnumSingleTon");// 获取Class对象
Method[] methods = obj.getDeclaredMethods();
for (Method method : methods)
{
System.out.println(method); // 没有任何构造方法的项目
}
Constructor<?>[] declaredMethods = obj.getDeclaredConstructors();
System.out.println(declaredMethods.length); // 输出1
for (Constructor<?> constructor : declaredMethods)
{
System.out.println(constructor);
}
Constructor<?> constructor = obj.getDeclaredConstructor(String.class, int.class, int.class);// 报异常java.lang.NoSuchMethodException
constructor.setAccessible(true);// 将私有构造器的访问模式改为可以访问
Object newInstance = constructor.newInstance();// 使用构造器创建一个新对象,报异常
// java.lang.IllegalArgumentException:
// Cannot
// reflectively
// create enum
// objects
System.out.println(newInstance);// 打印对象的地址
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static com.jnk.think.java.chapter.reflect.EnumSingleTon[] com.jnk.think.java.chapter.reflect.EnumSingleTon.values()
public static com.jnk.think.java.chapter.reflect.EnumSingleTon com.jnk.think.java.chapter.reflect.EnumSingleTon.valueOf(java.lang.String)
public void com.jnk.think.java.chapter.reflect.EnumSingleTon.increment()
public int com.jnk.think.java.chapter.reflect.EnumSingleTon.getTimes()
private void com.jnk.think.java.chapter.reflect.EnumSingleTon.p()
1
private com.jnk.think.java.chapter.reflect.EnumSingleTon(java.lang.String,int,int)
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.jnk.think.java.chapter.reflect.Reflect.enumSingleTonTest(Reflect.java:90)
at com.jnk.think.java.chapter.reflect.Reflect.main(Reflect.java:38)
在使用反射机制获取枚举 EnumSingleTon的构造器的时候,可以得到这个构造器,但是在使用反射机制调用这个构造器的时候,就报异常:java.lang.IllegalArgumentException: Cannot reflectively create enum objects,不能使用反射机制创建一个枚举对象。
由于枚举类不能在外部实例化对象,并且无偿提供了序列化机制,绝对防止了多次实例化。单元素枚举已经成为实现SingleTon的最佳方式。