前言
单例大家估计都已经了解过了,这里就不多说了,贴一段概念吧:
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例。
JAVA中单例的实现方法一般是将构造方法私有化,然后就只有类本身才能new出来自己的实例了,最后后提供一个静态方法将自己new出来的实例返回出去。
单例代码示例
这里贴一段最简单的单例代码实现:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
大家应该都可以看出来,这是一个饿汉式单例的写法,这样写有没有问题呢?
肯定是有问题的,那就是通过反射来对对象进行实例化,这样系统中就会出现多个实例了,违反了单例设计的初衷。那有没有解决方法呢?
必须有的啊,下面就通过一场“战争”来详细描述单例和反射的博弈过程。
战争开始
第一回合(Singleton VS Destroyer)
单例军团派出了自己的普通士兵(Singleton):
/**
* 饿汉式单例初版
* @author ZhengNC
* @date 2020/5/22 11:32
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
反射军团也派出了自己的普通士兵(Destroyer):
/**
* 单例破坏者初版
* @author ZhengNC
* @date 2020/5/22 14:07
*/
public class Destroyer {
public static void main(String[] args){
destroy();
}
/**
* 破坏初版单例
* @throws Exception
*/
public static void destroy(){
//正常方式获取单例对象
Singleton instance = Singleton.getInstance();
Singleton other = null;
try {
//通过反射获取单例对象
Class clazz = Singleton.class;
Constructor<Singleton> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
other = constructor.newInstance();
}catch (Exception e){
System.out.println("抛出了异常:"+e.getCause().getMessage());
}
System.out.println(instance);
System.out.println(other);
if (other != null && instance != other){
System.out.println("破坏成功");
}else {
System.out.println("破坏失败");
}
}
}
结果(Destroyer胜利)
com.test.javademo.singleton.destroy.roundOne.Singleton@1ddc4ec2
com.test.javademo.singleton.destroy.roundOne.Singleton@133314b
破坏成功
反射军团(Destroyer)胜利!
第二回合(SingletonPlus VS Destroyer)
单例军团看到自己的普通士兵不堪一击,于是加强了自己的士兵,在构造方法里加入验证,派出了加强后的士兵(SingletonPlus):
/**
* 饿汉式单例加强版
* @author ZhengNC
* @date 2020/5/22 11:32
*/
public class SingletonPlus {
private static SingletonPlus instance = new SingletonPlus();
private SingletonPlus(){
//防止通过反射调用构造方法创建对象
if (instance != null){
throw new RuntimeException("不允许创建多个单例对象");
}
}
public static SingletonPlus getInstance(){
return instance;
}
}
反射军团决定乘胜出击,继续派出自己的普通士兵(Destroyer)跟单例军团的加强版士兵(SingletonPlus)战斗。PS:自己实验时别忘了将Destroyer里的Singleton改成SingletonPlus.
结果(SingletonPlus胜利)
抛出了异常:不允许创建多个单例对象
com.test.javademo.singleton.destroy.roundOne.SingletonPlus@1ddc4ec2
null
破坏失败
单例军团(SingletonPlus)胜利!
第三回合(SingletonPlus VS DestroyerPlus)
单例军团继续派出自己的加强版士兵(SingletonPlus)。
而反射军团也对自己的士兵进行了强化(将单例的校验参数改变之后再实例化对象),派出了加强型士兵(DestroyerPlus):
/**
* 单例破坏者增强版
* @author ZhengNC
* @date 2020/5/22 15:07
*/
public class DestroyerPlus {
public static void main(String[] args){
destroyPlus();
}
/**
* 破坏增强版单例
* @throws Exception
*/
public static void destroyPlus(){
//正常方式获取单例对象
SingletonPlus instance = SingletonPlus.getInstance();
SingletonPlus other = null;
try {
//通过反射获取单例对象
Class clazz = SingletonPlus.class;
Constructor<SingletonPlus> constructor = clazz.getDeclaredConstructor();
//通过设置属性值为空来绕过构造方法里的验证
Field field = clazz.getDeclaredField("instance");
field.setAccessible(true);
field.set(null, null);
constructor.setAccessible(true);
other = constructor.newInstance();
}catch (Exception e){
System.out.println("抛出了异常:"+e.getMessage());
}
System.out.println(instance);
System.out.println(other);
if (other != null && instance != other){
System.out.println("破坏成功");
}else {
System.out.println("破坏失败");
}
}
}
结果(DestroyerPlus胜利)
com.test.javademo.singleton.destroy.roundOne.SingletonPlus@b1bc7ed
com.test.javademo.singleton.destroy.roundOne.SingletonPlus@7cd84586
破坏成功
反射军团(DestroyerPlus)胜利!
第四回合(SingletonPlusPlus VS DestroyerPlus)
单例军团不服气,再次加强了自己的士兵(将校验参数进行final修饰),派出了再次加强版士兵(SingletonPlusPlus):
/**
* 饿汉式单例再次增强版
* @author ZhengNC
* @date 2020/5/22 11:32
*/
public class SingletonPlusPlus {
//加上final防止反射改变此属性值绕过验证
private static final SingletonPlusPlus instance = new SingletonPlusPlus();
private SingletonPlusPlus(){
//防止通过反射调用构造方法创建对象
if (instance != null){
throw new RuntimeException("不允许创建多个单例对象");
}
}
public static SingletonPlusPlus getInstance(){
return instance;
}
}
反射军团再次派出上一回合取得胜利的加强版士兵(DestroyerPlus)。PS:自己实验时别忘了将DestroyerPlus里的SingletonPlus改成SingletonPlusPlus.
结果(SingletonPlusPlus胜利)
抛出了异常:Can not set static final com.test.javademo.singleton.destroy.roundOne.SingletonPlusPlus field com.test.javademo.singleton.destroy.roundOne.SingletonPlusPlus.instance to null value
com.test.javademo.singleton.destroy.roundOne.SingletonPlusPlus@b1bc7ed
null
破坏失败
单例军团(SingletonPlusPlus)胜利!
第五回合(SingletonPlusPlus VS DestroyerPlusPlus)
单例军团继续派出自己的再次加强版士兵(SingletonPlusPlus)。
反射军团为了取得胜利已经不择手段,对造士兵的工厂也进行了改造(对反射本身进行反射改造,使其可以修改final修饰的变量),然后推出了再次加强版士兵(DestroyerPlusPlus):
/**
* 单例破坏者再次增强版
* @author ZhengNC
* @date 2020/5/22 15:07
*/
public class DestroyerPlusPlus {
public static void main(String[] args){
destroyPlusPlus();
}
/**
* 破坏再次增强版单例
* @throws Exception
*/
public static void destroyPlusPlus(){
//正常方式获取单例对象
SingletonPlusPlus instance = SingletonPlusPlus.getInstance();
SingletonPlusPlus other = null;
try {
//通过反射获取单例对象
Class clazz = SingletonPlusPlus.class;
Constructor<SingletonPlusPlus> constructor = clazz.getDeclaredConstructor();
//通过设置属性值为空来绕过构造方法里的验证
Field field = clazz.getDeclaredField("instance");
//通过修改反射类的属性,将修改final值设置为允许,以便继续修改final属性
Class fieldClazz = field.getClass();
Field modifyField = fieldClazz.getDeclaredField("modifiers");
modifyField.setAccessible(true);
System.out.println(field.getModifiers());
modifyField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
System.out.println(field.getModifiers());
field.setAccessible(true);
field.set(null, null);
constructor.setAccessible(true);
other = constructor.newInstance();
}catch (Exception e){
System.out.println("抛出了异常:"+e.getMessage());
}
System.out.println(instance);
System.out.println(other);
if (other != null && instance != other){
System.out.println("破坏成功");
}else {
System.out.println("破坏失败");
}
}
}
结果(DestroyerPlusPlus胜利)
com.test.javademo.singleton.destroy.roundOne.SingletonPlusPlus@7cd84586
com.test.javademo.singleton.destroy.roundOne.SingletonPlusPlus@30dae81
破坏成功
反射军团(DestroyerPlusPlus)取得胜利!
战争结果统计
回合 | 单例军团出战者 | 反射军团出战者 | 胜利者 |
---|---|---|---|
第一回合 | Singleton | Destroyer | Destroyer |
第二回合 | SingletonPlus(构造方法增加校验) | Destroyer | SingletonPlus |
第三回合 | SingletonPlus | DestroyerPlus(先修改校验参数) | DestroyerPlus |
第四回合 | SingletonPlusPlus(final修饰校验参数) | DestroyerPlus | SingletonPlusPlus |
第五回合 | SingletonPlusPlus | DestroyerPlusPlus(改造自己可以修改final修饰的参数) | DestroyerPlusPlus |
结语
记录一下自己学习单例的一个小过程,单例还有很多实现的方法,本文只使用了饿汉式单例来做示例。
后面的单例防御反射的方法暂时还没有想到,希望有知道的读者不吝赐教,非常感激。
再次感谢每一个读我的文章的大牛,谢谢!