当前位置: 首页 > 知识库问答 >
问题:

获取java的声明字段。朗。反思。jdk12中的字段

邓光赫
2023-03-14

在java8中,可以访问java类的字段。朗。反思。使用例如。

Field.class.getDeclaredFields();

在java12(从java9开始?)这只返回一个空数组。这不会改变,即使

--add-opens java.base/java.lang.reflect=ALL-UNNAMED

设置。

有什么办法可以做到这一点吗?(显然,这可能不是一个好主意,我希望在junit测试期间通过反射更改代码中的“static final”字段

Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(myfield, myfield.getModifiers() & ~Modifier.FINAL);

)

共有3个答案

严修诚
2023-03-14

我找到了一种方法,它在JDK 8,11,17上工作。

Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0.setAccessible(true);
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
Field modifiers = null;
for (Field each : fields) {
    if ("modifiers".equals(each.getName())) {
        modifiers = each;
        break;
    }
}
assertNotNull(modifiers);

使用JDK 11或更高版本时,不要忘记设置以下参数:

--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
邢晗日
2023-03-14
匿名用户

你不能。这是故意做的改变。

例如,如果您想将其用于测试目的,您可以使用PowerMock,并且它是在它的引擎盖下使用jav辅助(字节码操作)。这正是评论中的错误建议做的。

换句话说,既然java-12,就没有办法通过普通java访问它。

曹成双
2023-03-14

这在Java12中不再起作用的原因是JDK-8210522。该CSR表示:

Core reflection有一种过滤机制,可以从类getXXXField和getXXXMethod中隐藏对安全性和完整性敏感的字段和方法。过滤机制已用于多个版本,以隐藏安全敏感字段,如系统。安全和阶级。类加载器。

该CSR建议扩展过滤器,以隐藏java.lang.reflect和java.lang.invoke.中许多高度安全敏感类的字段

java中的许多类。lang.reflect和java。invoke包具有私有字段,如果直接访问这些字段,将危及运行时或使VM崩溃。理想情况下,java中类的所有非公共/非保护字段。base将通过核心反射进行过滤,并且不能通过不安全的API进行读/写,但目前我们还没有做到这一点。同时,过滤机制被用作创可贴。

将筛选器扩展到以下类中的所有字段:

java.lang.ClassLoader
java.lang.reflect.AccessibleObject
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method

以及java中的私有字段。lang.invoke。方法句柄。用于查找类和访问模式的查找。

没有规范更改,这是对非公共/非保护字段的过滤,在java之外没有任何内容。基地应依托。没有一个类是可序列化的。

基本上,它们过滤掉了java.lang.reflect.Field的字段,所以您不能滥用它们——就像您目前正在尝试的那样。你应该找到另一种方法来做你需要的事情;尤金的答案似乎至少提供了一个选择。

注意:上面的CSR表明最终目标是防止对java内部代码的所有反射访问。基本模块。然而,这种过滤机制似乎只影响核心反射API,可以通过使用Invoke API来解决。我不太清楚这两个API是如何关联的,因此,如果除了更改静态最终字段的可疑性之外,这不是期望的行为,那么应该有人提交错误报告(首先检查现有的错误报告)。换言之,使用以下黑客,风险自负;试着找到另一种方法来做你首先需要的事情。

这就是说,至少在OpenJDK 12.0中,看起来您仍然可以侵入修饰符字段。1、使用java。lang.invoke。VarHandle

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public final class FieldHelper {

    private static final VarHandle MODIFIERS;

    static {
        try {
            var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
            MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class);
        } catch (IllegalAccessException | NoSuchFieldException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void makeNonFinal(Field field) {
        int mods = field.getModifiers();
        if (Modifier.isFinal(mods)) {
            MODIFIERS.set(field, mods & ~Modifier.FINAL);
        }
    }

}

下面使用上述方法更改ArrayList中的静态finalEMPTY\u ELEMENTDATA字段。当使用0的容量初始化ArrayList时,使用此字段。最终结果是创建的ArrayList包含元素,而没有实际添加任何元素。

import java.util.ArrayList;

public class Main {

    public static void main(String[] args) throws Exception {
        var newEmptyElementData = new Object[]{"Hello", "World!"};
        updateEmptyElementDataField(newEmptyElementData);

        var list = new ArrayList<>(0);

        // toString() relies on iterator() which relies on size
        var sizeField = list.getClass().getDeclaredField("size");
        sizeField.setAccessible(true);
        sizeField.set(list, newEmptyElementData.length);

        System.out.println(list);
    }

    private static void updateEmptyElementDataField(Object[] array) throws Exception {
        var field = ArrayList.class.getDeclaredField("EMPTY_ELEMENTDATA");
        FieldHelper.makeNonFinal(field);
        field.setAccessible(true);
        field.set(null, array);
    }

}

输出:

[Hello, World!]

根据需要使用--添加打开的

 类似资料:
  • 问题内容: 有什么方法可以使用反射来按声明的顺序获取声明的类的字段(和方法)?根据该文件,方法和字段的顺序返回的,等不确定。 如Java反射中所建议的那样,可以使用注释来指定类似索引的内容:类字段和方法的顺序是否标准化? 有没有更好的选择,即不必手动指定索引? 现在,在您问我需要什么之前:我们有一个方法将一个很大的数据结构作为输入并对其进行冗长的计算。为了创建单元测试,我们制作了一个方法,该方法采

  • 我已经建立了一个字段声明列表,需要找出每个字段的名称: 我找不到任何检索字段名称的方法。 奇怪的是,其中一个构造函数确实接受一个字段参数,所以不确定为什么它没有getter。我需要从另一个节点获取它吗? FieldDeclaration(节点列表修饰符、类型、字符串名称)创建一个FieldDeclaration。https://www.javadoc.io/doc/com.github.javap

  • 问题内容: 在没有泛型类型的类中,我想声明一个类似于以下内容的相当复杂的泛型字段: 问题是Java编译器不会让我:) 所以我的问题是如何正确地引入T和S,而又不向类Client添加类型。 我的目标是强制成为您选择的类的子类型和作为子类。 问题答案: 你不能 唯一的选择是在类声明中声明泛型类型参数。如果您的类没有泛型类型参数,则其成员不能为泛型。您必须在类成员的声明中使用实际类型。

  • 问题内容: 我有一个具有String字段的对象。我可以通过致电获得该字段: 我设置了一个方法来设置此实例的字段的值,但是各个getter似乎是,这很奇怪,因为我希望它是。 如何获取实例的值? 问题答案: 如果使用,则“ setter”为,“ getter”为。在 这两种 情况下,第一个参数都是您要在其上访问字段的实例。

  • 问题内容: 我有以下课程: 是否可以使用反射仅获取静态字段的列表?我知道我可以使用来获得所有字段的数组。但是似乎无法确定实例是否代表静态字段。 问题答案: 您可以这样做:

  • 问题内容: 我试图通过反射接收字段值。问题是我不知道字段类型,必须在获取值时确定它。 这段代码会导致以下异常: 无法将java.lang.String字段com .... fieldName设置为java.lang.String 我尝试进行转换,但出现编译错误: 要么 我怎样才能做到这一点? 问题答案: 像之前回答的那样,你应该使用: 有时更喜欢的另一种方法是动态调用getter。示例代码: 还应