我试图用反射改变private static final
字段的值(是的,这可能是一个非常糟糕的想法,我知道)。而且,在大多数情况下,使用以下代码可以正常工作:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class A {
public static void main(String[] args) throws ReflectiveOperationException {
System.out.println("Before :: " + B.get());
Field field = B.class.getDeclaredField("arr");
field.setAccessible(true);
// System.out.println("Peek :: " + ((String[]) field.get(null))[0]);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, new String[] { "Good bye, World!" });
System.out.println("After :: " + B.get());
}
}
class B {
private static final String[] arr = new String[] { "Hello, World!" };
public static String get() {
return arr[0];
}
}
按预期打印:
Before :: Hello, World!
After :: Good bye, World!
当我在get
ting之前通过反射尝试set
字段值时出现问题。也就是说,如果我取消注释上面示例中的注释行,我会得到以下内容:
Before :: Hello, World!
Peek :: Hello, World!
Exception in thread "main" java.lang.IllegalAccessException: Can not set static final [Ljava.lang.String; field B.arr to [Ljava.lang.String;
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:764)
at A.main(A.java:14)
为什么会这样?在打电话给accessible
之后我试图再次设置get
旗帜,但它没有帮助。我尝试了许多其他似乎没有帮助的事情......
谢谢你的帮助!
编辑:Using reflection to change static final File.separatorChar for unit testing?中有一个答案元素(参见@Rogério的“重要更新”)。
重要更新:上述解决方案并非在所有情况下都有效。如果在重置之前可以访问该字段并通过Reflection读取,则会抛出
IllegalAccessException
。它失败是因为Reflection API创建了内部FieldAccessor
对象,这些对象被缓存并重用(请参阅java.lang.reflect.Field#acquireFieldAccessor(boolean)实现)。示例测试代码失败:Field f = File.class.getField("separatorChar"); f.setAccessible(true); f.get(null); // call setFinalStatic as before: throws IllegalAccessException
可悲的是,它没有说明如何解决这个问题......我如何“重置”这个领域?
在回答这个问题时,我不确定应该对细节有多深入。但这里有一个简短的总结:
当您执行反射性Field#get
调用时,调用将在内部(在几次安全检查之后)委派给sun.reflect.FieldAccessor
。这是一个内部接口,顾名思义,它提供对字段值的访问。内部使用的FieldAccessor
实例是懒惰创建的,“缓存”供以后使用,甚至在多个Field
实例之间共享。
FieldAccessor
接口有许多不同的实现。这些实现专门用于通过调用setAccessible(true)
可访问的普通字段,静态字段或私有字段的各种情况。例如,在你的情况下,有一个UnsafeQualifiedStaticObjectFieldAccessorImpl
涉及,名称已经表明这只是几十个专业化之一。
许多这些FieldAccessor
实现存储了一个内部状态,它描述了该字段的一些属性。例如,字段是“只读”还是final
(!)。
重点是:当您进行反射性FieldAccessor
调用时创建的Field#get
与稍后用于反射性Field#set
调用的相同。但是当FieldAccessor
被创建时,该领域仍然被认为是final
。您只有在创建FieldAccessor
后才能更改此设置。
因此,最简单的解决方案是确保在执行第一次反射调用之前更改字段的final
状态。这样,内部创建的FieldAccessor
就是在“非最终”字段上运行的import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class A {
public static void main(String[] args) throws Exception {
System.out.println("Before :: " + B.get());
Field field = B.class.getDeclaredField("arr");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
System.out.println("Peek :: " + ((String[]) field.get(null))[0]);
field.set(null, new String[] { "Good bye, World!" });
System.out.println("After :: " + B.get());
}
}
class B {
private static final String[] arr = new String[] { "Hello, World!" };
public static String get() {
return arr[0];
}
}
:
brutallyHackYourWayThroughInternalClasses
我不应该提这个。人们会这样做。但无论如何:
从技术上讲,也可以FieldAccessor
并修改内部创建的final
,以便之后允许修改字段,即使它最初是为字段的import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class A {
public static void main(String[] args) throws Exception {
System.out.println("Before :: " + B.get());
Field field = B.class.getDeclaredField("arr");
field.setAccessible(true);
System.out.println("Peek :: " + ((String[]) field.get(null))[0]);
brutallyHackYourWayThroughInternalClasses(field);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, new String[] { "Good bye, World!" });
System.out.println("After :: " + B.get());
}
private static void brutallyHackYourWayThroughInternalClasses(Field field)
throws Exception
{
Field overrideFieldAccessorField =
Field.class.getDeclaredField("overrideFieldAccessor");
overrideFieldAccessorField.setAccessible(true);
Object overrideFieldAccessorValue =
overrideFieldAccessorField.get(field);
Class<?> unsafeFieldAccessorImplClass =
Class.forName("sun.reflect.UnsafeFieldAccessorImpl");
Field isFinalField =
unsafeFieldAccessorImplClass.getDeclaredField("isFinal");
isFinalField.setAccessible(true);
isFinalField.set(overrideFieldAccessorValue, false);
Class<?> unsafeQualifiedStaticFieldAccessorImplClass =
Class.forName("sun.reflect.UnsafeQualifiedStaticFieldAccessorImpl");
Field isReadOnlyField =
unsafeQualifiedStaticFieldAccessorImplClass.getDeclaredField(
"isReadOnly");
isReadOnlyField.setAccessible(true);
isReadOnlyField.set(overrideFieldAccessorValue, false);
}
}
class B {
private static final String[] arr = new String[] { "Hello, World!" };
public static String get() {
return arr[0];
}
}
版本创建的:
qazxswpoi