你的单例真的写对了吗?能经受住反射的考验吗

2023-12-16 06:18

本文主要是介绍你的单例真的写对了吗?能经受住反射的考验吗,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在我们日常的用法以及面试过程中,说到写单例,绝大部分都是采用二段锁写的,代码如下:

public class Singleton {private volatile static Singleton singleton;/*** 私有构造器*/private Singleton() {}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton ==  null) {singleton = new Singleton();}}}return singleton;}
}

下面来看一段调用:

 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {Singleton singleton = Singleton.getInstance();System.out.println(singleton);Singleton reflectSingleton = (Singleton) Class.forName("org.white.whiteroot.Singleton").newInstance();System.out.println(reflectSingleton);System.out.println(singleton.equals(reflectSingleton));}

运行结果如下:

org.white.whiteroot.Singleton@4a574795
org.white.whiteroot.Singleton@f6f4d33
false

可以看到我们常自以为没问题的单例,很容易就被反射攻破了。

那么解决方案如何呢,我们都知道,反射其实还是会调用私有的无参数构造器,那么我们做如下改动如何(很多网上的结论都是下面这种方式):

// 新增属性
private static boolean constructed;
// 改写私有构造方法
private Singleton() {if (constructed) {throw new RuntimeException("Singleton has been constructed");}constructed = true;
}

此时再调用上面的测试方法,执行结果如下:

org.white.whiteroot.Singleton@4a574795
Exception in thread "main" java.lang.RuntimeException: Singleton has been constructed

可以看到通过反射构造失败了。
不过既然我们在用反射进行破坏,那么上面代码能经受住反射的考验吗?此处敲黑板。我们修改测试方法如下:

 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {Singleton singleton = Singleton.getInstance();System.out.println(singleton);Class reflectSingletonClass = Class.forName("org.white.whiteroot.Singleton");Field field = reflectSingletonClass.getDeclaredField("constructed");field.setAccessible(true);field.set(singleton, false);Singleton reflectSingleton = (Singleton) reflectSingletonClass.newInstance();System.out.println(reflectSingleton);System.out.println(singleton.equals(reflectSingleton));}

再次运行,结果如下:

org.white.whiteroot.Singleton@4a574795
org.white.whiteroot.Singleton@23fc625e
false

我们发现,我们的单例再次被破坏了!
在我们日常编码中,很多事情都要动手去做,凡事多想一步。网上很多方案中都没有去尝试通过反射修改属性,这其实是一种不负责任的做法。
另外在overstackflow上有一种取巧的做法,不过也会存在反射的问题,其做法如下:

    private Singleton() {if (singleton != null) {throw new RuntimeException("Singleton has been constructed");}}

这种做法依旧会存在一个问题,这个问题其实在以上所有方法中都存在:

Class reflectSingletonClass = Class.forName("org.white.whiteroot.Singleton");
Field field = reflectSingletonClass.getDeclaredField("singleton");
field.setAccessible(true);
field.set(singleton, null);

通过如上方法在反射中讲singleton的值置为空,这样相当于完全破换掉单例,每次置空后通过调用getInstance方法都会生成新的对象

那么有没有不会被破坏的方式呢?答案当然是有的,从如下两个方案:

  1. 既然破坏单例都是通过反射破坏属性后调用无参构造器,那么我们可以把属性放到第三方去托管,比如把singleton放到redis托管,每次调用序列化回来,这样除非去攻击托管的地方,否则是改变不了咱们的单例的。
  2. 反射必定还是会调用构造器,那么咱们可以通过不调用构造器的方式来做单例对象,那就是枚举。

总结一句:目前来讲,通过枚举来防止反射是最优解决方案。

这篇关于你的单例真的写对了吗?能经受住反射的考验吗的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/499434

相关文章

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Java反射实现多属性去重与分组功能

《Java反射实现多属性去重与分组功能》在Java开发中,​​List是一种非常常用的数据结构,通常我们会遇到这样的问题:如何处理​​List​​​中的相同字段?无论是去重还是分组,合理的操作可以提高... 目录一、开发环境与基础组件准备1.环境配置:2. 代码结构说明:二、基础反射工具:BeanUtils

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)

C#特性(Attributes)和反射(Reflection)详解

《C#特性(Attributes)和反射(Reflection)详解》:本文主要介绍C#特性(Attributes)和反射(Reflection),具有很好的参考价值,希望对大家有所帮助,如有错误... 目录特性特性的定义概念目的反射定义概念目的反射的主要功能包括使用反射的基本步骤特性和反射的关系总结特性

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

Java通过反射获取方法参数名的方式小结

《Java通过反射获取方法参数名的方式小结》这篇文章主要为大家详细介绍了Java如何通过反射获取方法参数名的方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、解决方式方式2.1: 添加编译参数配置 -parameters方式2.2: 使用Spring的内部工具类 -

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取