Java反射-如果以“ get”操作开头,则在“ set”上发生IllegalAccessException

洛朗·查伯特(Laurent Chabot):

我试图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

可悲的是,它没有说明如何解决它……我如何“重置”该字段?

Marco13:

确实,这是一个非常糟糕的主意。

我不确定回答这个问题时应该深入细节的程度。但是,这是发生的简短摘要:

当您进行反射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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Java Telegram Bot发生了非法的反射访问操作

检查段落是否以字符开头,如果是,则在其上应用HTML样式

具有JAVA版本10的JMeter 4:发生了非法的反射访问操作

java,如果操作并返回

Java正则表达式:如果缺少则在字符串的开头和结尾添加引号

如果是PUT或DELETE请求,则在任何http请求上都会发生“连接重置”

Kotlin反射与Java的互操作性

Java 8 Lambdas上的反射类型推断

在实例化对象上执行Java反射

从类名Java反射GET构造失败

Java可选Get(如果存在)

如果发生OutOfMemoryError,则Java进程的行为

如果java.lang.OutOfMemoryError即将发生

如果在 google mock 上指定多个 WillRepeatedly 操作会发生什么?

如果找到一个Java类是final JNI中使用反射

如果<something>,则在Kusto中不执行任何操作

Windows 2012上的声纳尔,发生了非法的反射访问操作

如果是这样,则该样式规则在类上

如果鼠标进入特定区域,则在mousemove上隐藏元素

如果用户未登录,则在Link Press上打开Modal

如果选择了一行,则在 NSTableView 上显示菜单

如果先前的记录相同,则在表上隐藏结果

如果为空,则在prev()上设置类(jQuery)

如果在promise.set_value()之后调用future.get()会发生什么?

Java 10中的非法反射访问操作警告

在Kotlin数据类上无法通过反射找到Java批注

无法通过Java 8上的反射调用HashMap的getEntry

使用反射时出现IllegalAccessException

Java反射:(Type)field.get(object)-未经检查的转换