Java Reflection,更改私有静态final字段没有做任何事情

时间:2012-02-22 08:29:04

标签: java reflection static field final

我正在尝试使用Java Reflection更改私有静态final字段。 但它只是失败了。有人有想法吗?

这是我的代码:

public class SecuredClass {
    private static final String securedField = "SecretData";
    public static String getSecretData() { return securedField; }
}

public class ChangeField {
    static void setFinalStatic(Field field, Object newValue) throws Exception
    {
        field.setAccessible(true);

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

        field.set(null, newValue);
    }

    public static void main(String args[]) throws Exception
    {
        System.out.println("Before = " + SecuredClass.getSecretData());

        Field stringField = SecuredClass.class.getDeclaredField("securedField");
        stringField.setAccessible(true);
        setFinalStatic(stringField, "Screwed Data!");


        System.out.println("After = " + Java_FileMerger_Main.getSecretData());
    }
}

这是我的输出:

Before = SecretData
After = SecretData

我尝试使用System.setSecurityManager(null)删除SecurityManager; 但它没有改变任何东西。 我知道这是邪恶的,但我想让它发挥作用。 我希望有人可以帮助我。

3 个答案:

答案 0 :(得分:6)

未定义通过反射更改最终字段的语义(参见specification)。无法保证在从现场阅读时会看到对最终场的反射写入。

特别是对于具有常量分配的静态final字段(如在您的情况下),编译器通常会内联常量,因此在运行期间根本不会访问该字段(仅当您使用反射时)。

此处与SecurityManager无关。如果SecurityManager禁止使用反射,则操作将抛出异常,而不是静默失败。

问题Change private static final field using Java reflection的答案提供了更多细节,包括一个示例,您可以在字节代码中看到常量被内联。

答案 1 :(得分:1)

我也遇到了上面给出的例子的问题。 对我而言,在我没有将String定义为文字但作为对象之后,它才起作用。

private static final String SECURED_FIELD = String.valueOf("SecretData");

或者

private static final String SECURED_FIELD = new String("SecretData");

如果你想从文字中受益,这不是最好的解决方案,但我不必关心它。

答案 2 :(得分:1)

有一个解决方法。如果你在静态{}块中设置私有静态最终字段的值,它将起作用,因为它不会内联字段:

private static final String MY_FIELD;

static {
    MY_FIELD = "SomeText"
}

...

Field field = VisitorId.class.getDeclaredField("MY_FIELD");

field.setAccessible(true);
field.set(field, "fakeText");