我试图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
字段值进行反射之前,我会遇到问题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
但这无济于事。我尝试了许多其他似乎也无济于事的事情...
谢谢你的帮助!
编辑:在使用反射更改静态最终File.separatorChar用于单元测试中,有一个答案元素?(请参阅@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
,确保更改字段的-status 。这样,内部创建的就是在“非最终”字段上运行的: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];
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句