使用Java反射更改私有静态final字段 [英] Change private static final field using Java reflection

查看:708
本文介绍了使用Java反射更改私有静态final字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有私有静态最终字段的类,不幸的是,我需要在运行时更改。

I have a class with a private static final field that, unfortunately, I need to change at run-time.

使用反射我收到此错误: java.lang.IllegalAccessException:无法设置静态最终布尔字段

Using reflection I get this error: 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 ,并实际修改私有静态最终字段。

Assuming no SecurityManager is preventing you from doing this, you can use setAccessible to get around private and resetting the modifier to get rid of final, and actually modify a private static final field.

以下是一个例子:

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"
   }
}

假设没有<$抛出c $ c> SecurityException ,上面的代码打印一切都是真的

Assuming no SecurityException is thrown, the above code prints "Everything is true".

这里实际完成的内容如下:

What's actually done here is as follows:


  • 原始 boolean true false main 中被自动装箱到引用类型 Boolean 常量 Boolean.TRUE Boolean.FALSE

  • 反射用于更改 public static final Boolean.FALSE 引用 Boolean 引用Boolean.TRUE

  • 结果,随后每当 false 自动装箱到 Boolean.FALSE ,它引用与布尔相同的布尔 C $ C> Boolean.TRUE

  • false的所有内容现在都是true

  • The primitive boolean values true and false in main are autoboxed to reference type Boolean "constants" Boolean.TRUE and Boolean.FALSE
  • Reflection is used to change the public static final Boolean.FALSE to refer to the Boolean referred to by Boolean.TRUE
  • As a result, subsequently whenever a false is autoboxed to Boolean.FALSE, it refers to the same Boolean as the one refered to by Boolean.TRUE
  • Everything that was "false" now is "true"

  • < a href =https://stackoverflow.com/questions/2474017/using-reflection-to-change-static-final-file-separatorchar-for-unit-testing/2474242#2474242>使用反射来改变 static final File.separatorChar 用于单元测试

  • 如何将setAccessible限制为仅仅合法使用?


    • 有乱码 Integer 的缓存,改变 String 等的例子

    • Using reflection to change static final File.separatorChar for unit testing
    • How to limit setAccessible to only "legitimate" uses?
      • Has examples of messing with Integer's cache, mutating a String, etc

      每当你做这样的事情时都要特别小心。它可能不起作用,因为可能存在 SecurityManager ,但即使它没有,取决于使用模式,它可能会也可能不起作用。

      Extreme care should be taken whenever you do something like this. It may not work because a SecurityManager may be present, but even if it doesn't, depending on usage pattern, it may or may not work.


      JLS 17.5.3最终字段的后续修改

      在某些情况下,例如反序列化,系统将需要更改 final 构造后对象的字段。 final 字段可以通过反射和其他依赖于实现的方式进行更改。唯一具有合理语义的模式是构造对象然后更新对象的 final 字段的模式。该对象不应该对其他线程可见,也不应该读取 final 字段,直到 final的所有更新对象的字段已完成。冻结 final 字段出现在构造函数的末尾,其中设置了 final 字段,并且紧接着通过反射或其他特殊机制对 final 字段进行每次修改。

      In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation dependent means. The only pattern in which this has reasonable semantics is one in which an object is constructed and then the final fields of the object are updated. The object should not be made visible to other threads, nor should the final fields be read, until all updates to the final fields of the object are complete. Freezes of a final field occur both at the end of the constructor in which the final field is set, and immediately after each modification of a final field via reflection or other special mechanism.

      即使这样,也会出现许多并发症。如果 final 字段在字段声明中初始化为编译时常量,则对 final 字段的更改可能不会可以观察到,因为 final 字段的使用在编译时被编译时常量替换。

      Even then, there are a number of complications. If a final field is initialized to a compile-time constant in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the compile-time constant.

      另一个问题是该规范允许积极优化 final 字段。在一个线程中,允许重新排序 final 字段的读取,其中包含在构造函数中不会发生的最终字段的修改。

      Another problem is that the specification allows aggressive optimization of final fields. Within a thread, it is permissible to reorder reads of a final field with those modifications of a final field that do not take place in the constructor.



      参见




      • JLS 15.28常量表达式


        • 这种技术不太可能与原始的私有静态最终布尔值一起使用,因为它可以作为编译时常量内联,因此是新值可能无法观察

        • See also

          • JLS 15.28 Constant Expression
            • It's unlikely that this technique works with a primitive private static final boolean, because it's inlineable as a compile-time constant and thus the "new" value may not be observable
            • 基本上,

              field.getModifiers() & ~Modifier.FINAL
              

              关闭与 Modifier.FINAL相对应的位来自 field.getModifiers()& 是按位 - 而且是按位补码。

              turns off the bit corresponding to Modifier.FINAL from field.getModifiers(). & is the bitwise-and, and ~ is the bitwise-complement.

              • Wikipedia/Bitwise operation

              仍然无法解决这个问题?已经像我一样陷入了萧条?你的代码是这样的吗?

              Still not being able to solve this?, have fallen onto depression like I did for it? Does your code looks like this?

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

              阅读这个答案的评论,特别是@Pshemo的评论,它提醒我常数表达式是处理不同,因此不可能来修改它。因此,您需要将代码更改为:

              Reading the comments on this answer, specially the one by @Pshemo, it reminded me that Constant Expressions are handled different so it will be impossible to modify it. Hence you will need to change your code to look like this:

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

              如果您不是class ...我觉得你!

              有关此行为原因的详细信息读这个

              For more details about why this behavior read this?

              这篇关于使用Java反射更改私有静态final字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆