You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 17反射修改私有静态final字段失败:NoSuchFieldException求助

Java 17反射修改私有静态final字段报错NoSuchFieldException的解决方案

问题背景

之前在Java 11中可正常运行的反射修改私有静态final字段的代码,在Java 17中抛出java.lang.NoSuchFieldException: modifiers,即使添加启动参数--add-opens java.base/java.lang.reflect=ALL-UNNAMED也无法解决。

原代码如下:

public static void setFinalStatic(Field field, Object newValue) throws Exception {
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, newValue);
}

报错信息:

java.lang.NoSuchFieldException: modifiers
    at java.base/java.lang.Class.getDeclaredField(Class.java:2610)

问题根源

Java 16开始,JDK移除了Field类中的modifiers字段,改用内部的jdk.internal.reflect.FieldAccessor机制管理字段修饰符,直接通过反射获取modifiers字段的老方法彻底失效——这不是访问权限问题,而是字段本身已不存在,所以添加--add-opens参数也没用。

可行解决方案

方案1:使用Unsafe类(临时应急,不推荐生产)

Unsafe是JDK内部底层工具类,能绕过大部分访问限制,但依赖JVM内部实现,后续版本可能随时失效,仅建议临时测试用。代码示例:

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public static void setFinalStatic(Field field, Object newValue) throws Exception {
    field.setAccessible(true);
    // 获取Unsafe实例
    Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
    unsafeField.setAccessible(true);
    Unsafe unsafe = (Unsafe) unsafeField.get(null);
    // 获取静态字段在类中的内存偏移量
    long offset = unsafe.staticFieldOffset(field);
    // 直接修改内存中的值
    unsafe.putObject(field.getDeclaringClass(), offset, newValue);
}

注意:Java 9及以上版本需添加启动参数--add-opens java.base/sun.misc=ALL-UNNAMED才能访问Unsafe类。

方案2:针对非编译期常量的简化修改

如果目标静态final字段不是编译期常量(比如字段值是通过new创建的对象,而非直接字面量),JVM不会对其做内联优化,此时可跳过修改modifiers的步骤,直接调用field.set(null, newValue)就能生效。

比如原类中的字段定义:

public class TargetClass {
    // 非编译期常量,可直接修改
    public static final String TEST_VALUE = new String("original");
}

这种情况下,直接调用field.set(null, "new value")即可,无需处理modifiers。

但如果是public static final String TEST_VALUE = "original";这种编译期常量,JVM会把值直接内联到使用该字段的代码中,即使通过反射修改了字段值,代码读取到的还是原来的内联值,这种情况无法通过反射解决。

方案3:重构设计(推荐)

反射修改final字段本身属于违背Java设计原则的hack手段,后续JDK版本会持续收紧这类操作的权限。如果是自己维护的代码,建议:

  • 将需要修改的静态字段改为非final,通过封装方法控制修改逻辑
  • 使用配置类、依赖注入等方式管理可变的全局值
    如果是第三方类,考虑用包装类、代理类封装逻辑,绕过直接修改final字段的需求。

内容的提问来源于stack exchange,提问作者Kevin Chuang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:15:34