在java8中,可以使用例如
来访问类java.lang.reflect.Fields的字段。Field.class.getDeclaredFields();
在java12中(从java9开始),这仅返回一个空数组。甚至没有改变
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
设置。
任何想法如何实现这一目标? (Appart认为这可能不是一个好主意,我希望能够通过反射在junit测试期间更改代码中的“ static final”字段。Java8可以通过更改“修饰符”来实现
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(myfield, myfield.getModifiers() & ~Modifier.FINAL);
)
答案 0 :(得分:8)
在Java 12中不再起作用的原因是由于JDK-8210522。该企业社会责任说:
摘要
核心反射具有一种过滤机制,可从类getXXXField和getXXXMethod隐藏安全性和完整性敏感的字段和方法。过滤机制已用于多个版本中,以隐藏对安全敏感的字段,例如System.security和Class.classLoader。
此CSR建议扩展过滤器以隐藏来自java.lang.reflect和java.lang.invoke中许多高度安全敏感类的字段。
问题
java.lang.reflect和java.lang.invoke软件包中的许多类都有私有字段,如果直接访问这些私有字段,则会损害运行时或使VM崩溃。理想情况下,java.base中所有非公共/不受保护的类的字段都将通过核心反射进行过滤,并且无法通过Unsafe API进行读取/写入,但目前尚无此方法。同时,过滤机制被用作创可贴。
解决方案
将过滤器扩展到以下类别中的所有字段:
java.lang.ClassLoader java.lang.reflect.AccessibleObject java.lang.reflect.Constructor java.lang.reflect.Field java.lang.reflect.Method
以及用于查找类和访问方式的java.lang.invoke.MethodHandles.Lookup中的私有字段。
规格
没有任何规范更改,这是对java.base以外的任何非公共/不受保护的字段的过滤。这些类都不可序列化。
基本上,它们会过滤java.lang.reflect.Field
的字段,因此您不会滥用它们-正如您正在尝试的那样。您应该找到另一种方法来满足您的需求。 answer by Eugene似乎提供了至少一个选项。
注意:以上CSR指示最终目标是阻止java.base
模块内的所有内部代码反射访问。但是,此过滤机制似乎仅影响Core Reflection API,并且可以使用Invoke API来解决。我不确定这两个API之间的关系,因此,如果这不是人们所期望的行为,那么除了更改静态final字段的可疑性之外,还应该有人submit a bug report(首先检查是否存在一个)。换句话说,使用以下黑客程序后果自负;尝试找到另一种方法来做您首先需要做的事情。
也就是说,您似乎仍然可以使用modifiers
进入java.lang.invoke.VarHandle
字段,至少在OpenJDK 12.0.1中是如此。
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);
}
}
}
以下内容使用以上内容更改EMPTY_ELEMENTDATA
内的静态最终ArrayList
字段。初始化ArrayList
的容量0
时使用此字段。最终结果是创建的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!]
根据需要使用--add-opens
。
答案 1 :(得分:6)
不能。这是有意进行的更改。
例如,您可以使用PowerMock
并且它是@PrepareForTest
-如果您想将其用于测试目的,则可以使用javassist(字节码操作)。这正是注释中的错误建议采取的措施。
换句话说,由于java-12
-无法通过香草java进行访问。