当前位置: 首页 > 面试题库 >

使用Java反射更改私有静态最终字段

廖臻
2023-03-14
问题内容

我有一堂课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”。

实际执行的操作如下:

  • 基本boolean值true和falsein自动main装箱以引用类型Boolean“常量”,Boolean.TRUE并且Boolean.FALSE
  • 反射用于更改所public static final Boolean.FALSE引用Boolean的Boolean.TRUE
  • 结果,随后每当a false被自动装箱到时Boolean.FALSE,它所指的含义与所指的相同Boolean。Boolean.TRUE
  • “false”现在的一切都是”true”

相关问题

  • 使用反射更改static final File.separatorChar单元测试
  • 如何将setAccessible限制为仅“合法”使用?
  • 有实例弄乱Integer的缓存,使a发生突变String等

注意事项

每当你执行此类操作时,都应格外小心。它可能不起作用,因为SecurityManager可能存在a,但是即使它不存在,根据使用方式的不同,它可能也可能不起作用。

JLS 17.5.3随后对最终字段的修改

在某些情况下,例如反序列化,系统将需要final在构造后更改对象的字段。final可以通过反射和其他依赖实现的方式来更改字段。具有合理语义的唯一模式是构造一个对象,然后final更新该对象的字段的模式。在完成对对象字段的final所有更新之前,不应使该对象对其他线程可见,也不应读取final字段。final字段冻结发生在final设置该字段的构造函数的末尾,以及在final通过反射或其他特殊机制对字段进行每次修改之后立即冻结。

即使那样,仍然存在许多并发症。如果final在字段声明中将字段初始化为编译时常量,则final可能不会观察到对该字段的更改,因为final在编译时会将该字段的使用替换为编译时常量。

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

也可以看看

JLS 15.28常数表达式
这项技术不太可能与原始private static final boolean函数一起使用,因为它可以作为编译时常量内联,因此“ new”值可能无法观察到

附录:关于按位操作

实质上,

field.getModifiers() & ~Modifier.FINAL

关闭与Modifier.FINALfrom 相对应的位field.getModifiers()。&是按位与,并且~是按位补码。

也可以看看
维基百科/按位操作

记住常数表达式

仍然无法解决这个问题吗?像我一样,陷入了抑郁吗?你的代码看起来像这样吗?

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

阅读关于该答案的注释,特别是@Pshemo的注释,它使我想起了常量表达式的处理方式不同,因此将无法对其进行修改。因此,你需要将代码更改为如下所示:

public class A {
    private final String myVar;

    private A() {
        myVar = "Some Value";
    }
}


 类似资料:
  • 使用Java反射更改私有静态final字段 我按照上面链接中的说明使用java反射更改私有静态final字段。我有一个名为“数据”的对象在“data”内部,有一个名为“type”的私有静态最终变量我想将“type”设置为null。这是我的密码。 我试着在Java1.7上用类似的代码来做这件事,结果成功了。但是在Android上运行此代码会产生以下错误:java。lang.NoSuchFieldEx

  • 问题内容: 基于使用Java反射更改私有静态最终字段,我尝试设置私有静态最终字段。 (我知道这非常骇人,但是这个问题与代码质量无关;与Java反射有关。) 此打印 我已经使用OpenJDK 6和7,以及Oracle 7进行了尝试。 我不知道Java反射可以提供什么保证。但是,如果失败了,我以为会有一个(实际上所有反射方法都会抛出异常)。 这是怎么回事 问题答案: Java内联字段可在编译时初始化为

  • 我有两个UnitTest项目为我的Android项目。一个用于JUnit测试,一个用于Android Unit测试。在JUnit测试项目中,我创建了一个类来访问或设置私有字段、方法或构造函数。(附言:对于那些对完整代码感到好奇的人,请告诉我,我会把它添加到这篇文章的底部。) 我也有UnitTets来测试这些私有方法的访问。现在,所有这些UnitTest都可以工作,接受一个:设置final stat

  • 问题内容: 具体来说,我正在尝试为需要用于在Windows和Unix上构建路径的方法创建单元测试。该代码必须在两个平台上都可以运行,但是当我尝试更改此static final字段时,却遇到了JUnit错误。 任何人都知道发生了什么事吗? 当我这样做时,我得到 有什么想法吗? 问题答案: 从文档中: 如果基础字段是final,则该方法将为该字段成功地抛出除非, 并且该字段是非静态的 。 因此,在第一

  • 问题内容: 我有一个带有静态变量的Java类 如何使用反射访问对象? (我有字符串。我需要访问该对象。) 问题答案: 访问静态字段的方式与普通字段完全相同,只是不需要将任何参数传递给方法(可以传递null)。 试试这个:

  • } 当我做它将打印芒果,但它仍然在打印苹果。如果有人能帮助我只使用MOCKITO而不是PowerMock等。