如何用Java反射修改Random类让Math.random()每次返回相同值
普通反射无法直接修改方法内部的运行逻辑,但我们可以通过替换Math.random()依赖的底层随机数生成器实例,实现让它每次返回固定值的效果。
实现原理
Math.random()底层调用的是RandomNumberGeneratorHolder静态内部类中持有的randomNumberGenerator实例的nextDouble()方法,我们通过反射把这个静态final字段替换为自定义的Random子类实例,让自定义实例的nextDouble()固定返回同一个值即可。
可运行代码示例
import java.lang.reflect.Field; import java.util.Random; public class FixedRandomDemo { public static void main(String[] args) throws Exception { // 获取Math的私有静态内部类RandomNumberGeneratorHolder Class<?> holderClazz = Class.forName("java.lang.Math$RandomNumberGeneratorHolder"); // 获取内部类中存储随机数生成器的字段 Field rngField = holderClazz.getDeclaredField("randomNumberGenerator"); rngField.setAccessible(true); // 移除字段的final修饰符,才能修改静态final字段 Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(rngField, rngField.getModifiers() & ~java.lang.reflect.Modifier.FINAL); // 自定义Random子类,重写nextDouble固定返回相同值 Random fixedRng = new Random() { @Override public double nextDouble() { // 这里可以设置为任意你需要的固定double值 return 0.618; } }; // 替换原本的随机数生成器 rngField.set(null, fixedRng); // 测试,输出结果恒为true System.out.println(java.lang.Math.random() == java.lang.Math.random()); } }
兼容性说明
- 该方案适配Oracle JDK/OpenJDK 8及以上的标准JDK实现,非标准JDK如果修改了
Math类的内部实现,需要对应调整内部类和字段名称 - JDK 9及以上版本默认开启了模块访问限制,需要添加JVM启动参数
--add-opens java.base/java.lang=ALL-UNNAMED才能正常执行反射修改操作
内容的提问来源于stack exchange,提问作者Luka
相关产品推荐
相关产品推荐

