众所周知在编写Java项目中,常常会使用到单例模式,好比如创建对象型单例模式,但是这一种模式是可以使用反射机制进行破解的,这里称作为暴力反射,可以通过反编译创建多次对象,这里我们就用经典的饿汉式、懒汉式来进行实战,以及如何避免暴力反射。

先来一个简单的饿汉式模式:

public class Singleton {private Singleton(){}//饿汉式在程序加载之前实例已经存在到内存中,会浪费一些资源private static final  Singleton single = new Singleton();public Singleton getInstance(){return single;}
}

通过测试,两次获取的对象都是同一份,是没有问题的:

Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();//38997010 --- 38997010
System.out.println(singleton1.hashCode() + " --- " +  singleton2.hashCode());

接下来我们通过反射来破坏它,关闭私有构造器验证:

@Test
public void test3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);Singleton singleton = declaredConstructor.newInstance();Singleton singleton1 = declaredConstructor.newInstance();System.out.println(singleton.hashCode());  //38997010System.out.println(singleton1.hashCode());  //1942406066
}

可以看到通过反射关闭安全验证器后获取的两次或多次对象是不一样的,这也就是说明反射可以破环创建型单例模式。我们接着来懒汉式。

懒汉式代码:

public class LazSingleton {private LazSingleton() {}private volatile static LazSingleton lazSingleton;//如果单线程下的懒汉式是没有问题的,如果是多线程的情况下就会存在问题,我们使用双重检测锁public static LazSingleton getInstance(){if(lazSingleton == null){synchronized (LazSingleton.class){if(lazSingleton == null){lazSingleton = new LazSingleton(); //注意这里不是原子性操作,所以我们要使用 volatile关键字来修饰,实现了原子性操作 + 双重检测锁}}}return lazSingleton;}
}

通过测试,得出结论,反射也可以破坏懒汉式:

@Test
public void test4() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Constructor<LazSingleton> declaredConstructor = LazSingleton.class.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);LazSingleton singleton = declaredConstructor.newInstance();LazSingleton singleton1 = declaredConstructor.newInstance();System.out.println(singleton.hashCode());  //38997010System.out.println(singleton1.hashCode()); //1942406066
}

经过懒汉式、饿汉式的测试,我们都发现反射可以破解它,内部类同样也可以破解,这里不做演示,那么怎么样可以避免反射破坏呢? 这时可以使用枚举,枚举本身就是一个类,来自JDK1.5

使用枚举类来实现一个简单的单例模式:

public enum SingletonEnum {INSTANCE;public static SingletonEnum getInstance(){return INSTANCE;}
}

通过反射测试,发生错误:
java.lang.NoSuchMethodException: com.xiaowu.relaction.SingletonEnum.<init>()

@Test
public void test5() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Constructor<SingletonEnum> declaredConstructor = SingletonEnum.class.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);SingletonEnum singleton = declaredConstructor.newInstance();SingletonEnum singleton1 = declaredConstructor.newInstance();System.out.println(singleton.hashCode());System.out.println(singleton1.hashCode());
}

这时的报错信息为:找不到当前枚举类的构造方法,因为我们是通过获取类的构造器进行暴力反射的,我们通过反编译查看代码:

public final class SingletonEnum extends Enum
{public static SingletonEnum[] values(){return (SingletonEnum[])$VALUES.clone();}public static SingletonEnum valueOf(String name){return (SingletonEnum)Enum.valueOf(com/xiaowu/relaction/SingletonEnum, name);}private SingletonEnum(String s, int i){super(s, i);}public static SingletonEnum getInstance(){return INSTANCE;}public static final SingletonEnum INSTANCE;private static final SingletonEnum $VALUES[];static {INSTANCE = new SingletonEnum("INSTANCE", 0);$VALUES = (new SingletonEnum[] {INSTANCE});}
}

可以看到枚举类的反编译是有一个有参构造器的,参数为String、int,没有无参构造器,那么我们通过有参构造器获取对象:

    @Testpublic void test5() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Constructor<SingletonEnum> declaredConstructor = SingletonEnum.class.getDeclaredConstructor(String.class,int.class);declaredConstructor.setAccessible(true);SingletonEnum singleton = declaredConstructor.newInstance();SingletonEnum singleton1 = declaredConstructor.newInstance();System.out.println(singleton.hashCode());System.out.println(singleton1.hashCode());}

发生报错信息:无法反射地创建枚举对象,也就是说明枚举类不允许被反射破坏。

java.lang.IllegalArgumentException: Cannot reflectively create enum objects

底层有详细说明,Constructor.java:

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);}}if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");ConstructorAccessor ca = constructorAccessor;   // read volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst;}

所以,在项目中推荐使用枚举进行实现单例模式。

Java暴力反射Relaction相关推荐

  1. java 反射 私有成员_Java对类私有变量的暴力反射技术讲解

    Java对类私有变量的暴力反射 假设有一个类,他有一个私有变量: package com.howlaa.day04; public class ReflectPoint { private int p ...

  2. Java基础-day18-反射方法应用·暴力反射·内部类

    Day19 反射获取方法 getMethods()//获取所有可见的方法,包括继承的方法 getMethod(方法名,参数类型列表) getDeclaredMethods()//获取本类定义的的方法, ...

  3. Java资深反射玩家

    1.反射概述 2.获取Class类对象的三种方法 3.Class类的常用方法 4.反射获取构造方法并使用 5.反射获取类的成员属性并使用 6.反射获取类的成员方法并使用 7.反射的优缺点 8.反射的注 ...

  4. java f反射_java反射机制

    反射定义 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...

  5. [Java基础]反射获取构造方法并使用练习

    Student类代码如下: package ClassObjectPack;public class Student {private String name;int age;public Strin ...

  6. Java基础--反射

    1.概念 什么是反射? (1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法. 本质是JVM得到class对象之后,再通过class对象进行反编译,从而 ...

  7. java初反射_java中的反射机制

    前言: ​相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解AP ...

  8. Java中反射获取成员变量、构造方法、成员方法及类名

    都说反射是框架的灵魂,但是反射到底是啥呢,今天就聊聊反射的基础操作,也是必备操作. 反射机制是在程序运行时,对任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用他的任意一个属性和 ...

  9. java构造反射和函数_反射类的构造函数和方法

    开始学习Javaweb,看到了反射,现在还没有用上,先把基础的写上来吧 package com.april.write; public class Grade { private int num; p ...

最新文章

  1. swagger2的使用和swagger2markup离线文档的生成(最简单的方式)
  2. Java22-day13【Lambda表达式(标准格式、练习、注意事项)、接口组成更新(默认-静态-私有方法)、方法引用(构造器)】
  3. mac mongodb可视化工具_MongoDB从立地到成佛(介绍、安装、增删改查)
  4. crontab 周日_linux之crontab命令
  5. 在Azure平台上使用托管卡进行身份认证
  6. R语言入门(3)——R包的使用
  7. WinRAR压缩文件参数详解
  8. 常见的网上商城系统开发语言有哪些?
  9. SpringBoot实现接口签名防止篡改(V2)
  10. S7Comm Plus 协议研究
  11. 【Unity3D游戏教程】记忆翻牌游戏
  12. 快递面单成信息泄露重灾区,隐私面单成“必选项”
  13. 计算机学报在线阅读,计算机学报CHIN.pdf
  14. 为什么说串行比并行快?
  15. 我的世界java甘蔗机_我的世界全自动甘蔗机器制作教程
  16. 【Unity3D】游戏研发团队及岗位职责
  17. 国际音标(IPA)和美国音标(KK)对照表
  18. ​在职场,我们如果高效地沟通
  19. HandlerMethodReturnValueHandler处理返回值问题,aop
  20. Android保存搜索历史

热门文章

  1. centOS6.9调节亮度
  2. python定义字符串类型_Python数据类型之字符串
  3. c语言程序设计扫雷游戏实验报告,C语言程序设计扫雷游戏实验报告-20210415215509.pdf-原创力文档...
  4. jquery怎么实现页面刷新后保留鼠标点击addclass的样式
  5. 中首清算:直播新战事:腾讯推斗鱼虎牙合并,制衡B站、快手?
  6. HTTP400错误 请求无效 (Bad request)
  7. MindManager2021合理使用
  8. 安装Linux版的Notepad++
  9. vue项目优化 - 网站首屏加载时间
  10. java 货币符号,java – Android货币符号排序