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

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

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

为什么现在很多人愿意选择做债务重组?债重组真的就这么好吗?

债务重组,起初作为面向优质企业客户的定制化大额融资策略,以其高效周期著称,一个月便显成效。然而,随着时代的车轮滚滚向前,它已悄然转变为负债累累、深陷网贷泥潭者的救赎之道。在此路径下,个人可先借助专业机构暂代月供,经一段时间养护征信之后,转向银行获取低成本贷款,用以替换高昂网贷,实现利息减负与成本优化的双重目标。 尽管债务重组的代价不菲,远超传统贷款成本,但其吸引力依旧强劲,背后逻辑深刻。其一

使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统

【反射知识点详解】

Java中的反射(Reflection)是一个非常强大的机制,它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect包中的类和接口来实现。 通过反射,Java程序可以动态地创建对象、调用方法、访问字段,以及获取类的各种信息(如构造器、方法、字段等)。 反射的用途 反射主要用于以下几种情况: 动态创建对象:通过类的Class对象动态地创建其实例。访问类的字段

Go 在orm中使用反射

作为静态语言,golang 稍显笨拙,还好 go 的标准包reflect(反射)包弥补了这点不足,它提供了一系列强大的 API,能够根据执行过程中对象的类型来改变程序控制流。本文将通过设计并实现一个简易的 mysql orm 来学习它,要求读者了解mysql基本知识,并且跟我一样至少已经接触 golang 两到三个月。 orm 这个概念相信同学们都非常熟悉,尤其是写过rails的同学,对acti

类型信息:反射-Class

在说反射前提一个概念:RTTI(在运行时,识别一个对象的类型) public class Shapes {public static void main(String[] args) {List<Shape> shapes = Arrays.asList(new Circle(), new Square(), new Triangle());for (Shape shape : shapes

C#设计模式(1)——单例模式(讲解非常清楚)

一、引言 最近在学设计模式的一些内容,主要的参考书籍是《Head First 设计模式》,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了帮助我更深入地理解设计模式,二同时可以给一些初学设计模式的朋友一些参考。首先我介绍的是设计模式中比较简单的一个模式——单例模式(因为这里只牵涉到一个类) 二、单例模式的介绍 说到单例模式,大家第一

单例模式以及反射对单例模式的破坏及防御

单例模式(Singleton Pattern)是一种确保类在应用程序生命周期内只存在一个实例的设计模式。它不仅提供了全局访问点,还能节省内存、控制实例的生命周期。但常见的单例模式实现方式如饿汉式、懒汉式、双重校验锁、静态内部类等,虽然设计良好,但都容易被 Java 的反射机制所破坏。本文将介绍这些单例实现方式的优缺点、反射如何破坏它们的唯一性,以及如何防御这种破坏。 1. 单例模式的常见实现

线程--(1)单例模式创建

通过内部类创建单例模式 package com.flx.king.it_201707;/*** 公共:单例模式与多线程实现* * @author FuLX* @2017-7-26下午10:14:08* */public class Dome_02_SingletonAndManyThread {//1、定义一个内部类private static class Singletion{//创建单

1 单例模式(设计模式笔记)

1 单例模式 概述:使得一个类的对象成为系统中的唯一实例。 具体实现: 构造函数私有化 限制实例的个数 懒汉式(时间换空间) public class Singleton2 {public static Singleton2 singleton2;private Singleton2(){}public static Singleton2 getInstance() throws I