如何使用Java反射更改私有静态final字段?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (354)

有一个带有private static final字段的类,不幸的是,我需要在运行时更改。

使用反射我得到这个错误: java.lang.IllegalAccessException: Can not set static final boolean field

有没有办法改变价值?

Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
提问于
用户回答回答于

假设没有SecurityManager阻止你这样做,你可以使用setAccessible绕过private并重置修饰符来摆脱final,并实际修改一个private static final字段。

这是一个例子:

import java.lang.reflect.*;

public class EverythingIsTrue {
   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 {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);

      System.out.format("Everything is %s", false); // "Everything is true"
   }
}

假设没有SecurityException抛出,上面的代码打印出来"Everything is true"

这里实际做的是如下:

  • 原始booleantruefalsein main被自动装箱以引用类型Boolean“常量” Boolean.TRUEBoolean.FALSE
  • 反射用于更改public static final Boolean.FALSE引用所Boolean引用的Boolean.TRUE
  • 结果,随后每当a false被自动装箱时Boolean.FALSE,它指的是与Boolean所提到的相同Boolean.TRUE
  • "false"现在的一切都是"true"

相关问题

注意事项

每当你做这样的事情时,都应该格外小心。它可能不起作用,因为SecurityManager可能存在,但即使它不存在,取决于使用模式,它可能或可能不起作用。

JLS 17.5.3最终字段的后续修改 在某些情况下,例如反序列化,系统将需要final在构造之后更改对象的字段。final字段可以通过反射和其他依赖于实现的方式来改变。它具有合理语义的唯一模式是构造对象然后final更新对象字段的模式。对象不应该对其他线程可见,也不应该final读取字段,直到final完成对象字段的所有更新。final字段的冻结既发生在final设置字段的构造函数的末尾,也发生在final通过反射或其他特殊机制对字段进行每次修改之后。 即使这样,也有许多并发症。如果final字段在字段声明中初始化为编译时常量,则final可能无法观察到对字段的更改,因为final在编译时将该字段的使用替换为编译时常量。 另一个问题是规范允许积极优化final字段。在一个线程中,允许final使用构造函数中不发生的最终字段的那些修改来重新排序字段的读取。

也可以看看

  • JLS 15.28常量表达式
    • 这种技术不太可能适用于原语private static final boolean,因为它可以作为编译时常量内联,因此“新”值可能无法观察到

附录:关于按位操作

实质上,

field.getModifiers() & ~Modifier.FINAL

关断对应于比特Modifier.FINALfield.getModifiers()&是按位和,并且~是按位补码。

也可以看看

记住常量表达式

仍然无法解决这个问题?,就像我为此所做的那样陷入了萧条?你的代码看起来像这样吗?

public class A {
    private final String myVar = "Some Value";
}

阅读这个答案的评论,特别是@Pshemo的评论,它提醒我,Constant Expression的处理方式不同,因此无法对其进行修改。因此,您需要将代码更改为如下所示:

public class A {
    private final String myVar;

    private A() {
        myVar = "Some Value";
    }
}
用户回答回答于

从Java语言规范第17章第17.5.4节“写保护字段”中获得一点好奇心:

通常,可能不会修改最终和静态字段。但是,System.in,System.out和System.err是静态最终字段,由于遗留原因,必须允许通过方法System.setIn,System.setOut和System.setErr更改这些字段。我们将这些字段称为写保护,以区别于普通的最终字段。

资料来源:http//docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4

扫码关注云+社区

领取腾讯云代金券