讲师中心 微信公众号
AI工具推荐 视频效率加速

深入理解 Java 17 反射:解决 final static 字段修改问题

夜晨姑娘_7984

夜晨姑娘_7984

发布时间:2025-11-23 18:29:02

|

1528人浏览过

|

来源于php中文网

原创

深入理解 Java 17 反射:解决 final static 字段修改问题

java 17 对反射机制进行了调整,导致直接修改 `final static` 字段时可能遇到 `nosuchfieldexception`。本文将深入探讨这一变化的原因,并提供一个在 java 17 环境下通过反射安全修改 `final static` 字段的实用工作方案,包括必要的 jvm 启动参数和代码实现细节,帮助开发者克服反射操作的兼容性挑战。

Java 17 反射机制的变更背景

在 Java 11 及更早版本中,通过反射修改类的 private static final 字段是一种常见的操作,通常用于单元测试或框架层面的特殊需求。其核心在于获取 Field 对象的 modifiers 字段,并将其 FINAL 修饰符移除,从而允许修改其值。然而,自 Java 12 起,这一行为发生了变化。OpenJDK 的一个重要更新 (JDK-8210522) 导致 java.lang.reflect.Field 类中的 modifiers 字段不再能直接通过 getDeclaredField("modifiers") 访问。这是为了增强 JVM 的内部封装性,限制对内部实现细节的直接操作,以提高稳定性和安全性。

当尝试在 Java 17 中执行以下代码时,会抛出 NoSuchFieldException: modifiers 错误:

public static void setFinalStatic(Field field, Object newValue) throws Exception {
    field.setAccessible(true);
    // 这一行在 Java 17 中会抛出 NoSuchFieldException
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, newValue);
}

尽管尝试添加 --add-opens java.base/java.lang.reflect=ALL-UNNAMED 这样的 JVM 启动参数,也无法解决这个问题,因为问题并非出在模块访问权限,而是 modifiers 字段本身不再是 getDeclaredField 的可直接目标。

Java 17 环境下的解决方案

为了在 Java 17 及更高版本中实现对 final static 字段的反射修改,我们需要采用一种间接的方式来获取 modifiers 字段。这个工作方案利用了 Java 内部的 getDeclaredFields0 方法,它能返回一个类声明的所有字段,包括那些通常无法直接访问的内部字段。

Alibabacloud Sdk Client Initialization For Java
Alibabacloud Sdk Client Initialization For Java

在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。

下载

立即学习Java免费学习笔记(深入)”;

实现代码

以下是适用于 Java 17 的 setFinalStatic 方法实现:

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectionUtil {

    /**
     * 在 Java 17 及更高版本中,通过反射修改 final static 字段的值。
     * 此方法通过内部反射机制获取并修改 Field 对象的 modifiers 字段。
     *
     * @param field    要修改的 Field 对象。
     * @param newValue 字段的新值。
     * @throws Exception 如果反射操作失败。
     */
    public static void setFinalStatic(Field field, Object newValue) throws Exception {
        // 1. 确保字段可访问
        field.setAccessible(true);

        // 2. 获取 Class 类的 getDeclaredFields0 内部方法
        // 这个方法可以返回一个类声明的所有字段,包括内部字段
        Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
        getDeclaredFields0.setAccessible(true);

        // 3. 调用 getDeclaredFields0 方法,获取 Field.class 的所有字段
        // 目的是找到 Field 类内部的 "modifiers" 字段
        Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);

        // 4. 遍历找到名为 "modifiers" 的 Field 对象
        Field modifiersField = null;
        for (Field each : fields) {
            if ("modifiers".equals(each.getName())) {
                modifiersField = each;
                break;
            }
        }

        // 5. 确保找到了 modifiers 字段,并使其可访问
        if (modifiersField == null) {
            throw new NoSuchFieldException("Could not find 'modifiers' field in java.lang.reflect.Field class.");
        }
        modifiersField.setAccessible(true);

        // 6. 移除目标字段的 FINAL 修饰符
        // 通过位运算,将 FINAL 位(Modifier.FINAL)从 modifiers 字段中清除
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

        // 7. 设置目标字段的新值
        // 对于 static 字段,第一个参数为 null
        field.set(null, newValue);
    }

    // 示例用法
    public static final String MY_CONSTANT = "Original Value";

    public static void main(String[] args) throws Exception {
        System.out.println("原始值: " + MY_CONSTANT);

        Field myConstantField = ReflectionUtil.class.getDeclaredField("MY_CONSTANT");
        setFinalStatic(myConstantField, "Modified Value");

        System.out.println("修改后的值: " + MY_CONSTANT);
    }
}

必要的 JVM 启动参数

由于此解决方案涉及到对 java.lang.Class 内部方法的调用以及对 java.lang.reflect.Field 内部字段的修改,为了绕过 Java 模块系统的强封装限制,您需要在运行 JVM 时添加以下 —add-opens 参数:

--add-opens java.base/java.lang=ALL-UNNAMED \
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
  • --add-opens java.base/java.lang=ALL-UNNAMED: 允许未命名模块的代码通过反射访问 java.base 模块中的 java.lang 包的私有成员。这对于调用 Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class) 是必需的。
  • --add-opens java.base/java.lang.reflect=ALL-UNNAMED: 允许未命名模块的代码通过反射访问 java.base 模块中的 java.lang.reflect 包的私有成员。这对于修改 Field 对象的 modifiers 字段是必需的。

注意事项与总结

  1. 内部 API 使用风险: 这种解决方案依赖于 Java 内部 API (getDeclaredFields0 方法和 modifiers 字段)。内部 API 不保证在未来的 Java 版本中保持不变。这意味着您的代码在未来的 Java 版本中可能会再次失效。
  2. 安全性和稳定性: 绕过 final 关键字的语义通常被认为是一种不推荐的做法,因为它可能破坏程序的预期行为和安全性。除非有非常明确且充分的理由(如特殊的测试场景或框架级别的底层操作),否则应避免修改 final 字段。
  3. 替代方案: 在可能的情况下,优先考虑使用更标准的、非反射的编程实践。如果需要可变配置,请不要将其声明为 final。
  4. 模块化考量: Java 模块系统(Jigsaw)旨在加强封装。使用 —add-opens 参数本质上是在削弱这种封装。理解其含义并谨慎使用。

尽管存在上述风险,但对于那些需要在 Java 17 环境下继续执行 final static 字段反射修改的特定场景,本文提供的方案是一个有效的技术性工作。开发者应充分理解其原理和潜在影响,并权衡利弊后审慎采用。

热门AI工具

更多
AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
java中boolean的用法
java中boolean的用法

在Java中,boolean是一种基本数据类型,它只有两个可能的值:true和false。boolean类型经常用于条件测试,比如进行比较或者检查某个条件是否满足。想了解更多java中boolean的相关内容,可以阅读本专题下面的文章。

2070

2023.11.13

java boolean类型
java boolean类型

本专题整合了java中boolean类型相关教程,阅读专题下面的文章了解更多详细内容。

143

2025.11.30

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

3096

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

286

2025.12.06

NumPy性能优化版本更新与常见报错排查
NumPy性能优化版本更新与常见报错排查

本专题整理 NumPy 性能优化、版本更新与常见报错排查相关教程,覆盖向量化计算、广播性能、内存布局、NumPy 2.0 升级、版本兼容冲突、安装导入报错、dtype 溢出、矩阵运算异常和 broadcasting 报错修复,帮助读者系统掌握 NumPy 性能调优与问题定位方法。

0

2026.09.22

Vibeknow在线使用入口合集
Vibeknow在线使用入口合集

本专题汇总了Vibeknow在线创作视频的官方入口及网页版使用教程,涵盖PPT、PDF、Word等文档一键转讲解视频的核心操作,并整理了免费版水印规则与手机端浏览器访问指南,助你快速将知识内容视频化。

20

2026.09.21

NumPy随机数文件读写与dtype数据类型
NumPy随机数文件读写与dtype数据类型

本专题整理 NumPy 随机数、文件读写与 dtype 数据类型相关教程,覆盖 Generator/random、随机数种子、正态分布采样、npy/npz/CSV/TXT 保存读取、loadtxt/savetxt、memmap、大文件处理、astype 类型转换、结构化 dtype、整数溢出和精度丢失等场景。

20

2026.09.21

NumPy矩阵运算与线性代数计算
NumPy矩阵运算与线性代数计算

本专题整理 NumPy 矩阵运算与线性代数计算相关教程,覆盖矩阵乘法、dot 与 @ 运算符、逆矩阵、行列式、特征值与特征向量、SVD、线性方程组、欧氏距离、矩阵分解和大规模矩阵性能优化等内容,帮助读者掌握 np.linalg 与矩阵计算实战。

0

2026.09.21

NumPy广播机制数学运算与统计分析
NumPy广播机制数学运算与统计分析

本专题整理 NumPy 广播机制、数组数学运算与统计分析相关教程,覆盖广播规则、维度对齐、矩阵与数组加减除法、向量化计算、均值方差、分位数、中位数、直方图和 unique 频次统计等场景,帮助读者掌握 ndarray 高效计算与统计处理方法。

0

2026.09.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn